From d7b028d4cf0d8dfd72899eacbdca51585a845f1a Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:31:59 +0100 Subject: [PATCH 01/41] add comments and notes --- rollup_core/src/rollupdb.rs | 4 ++++ rollup_core/src/sequencer.rs | 23 ++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index b5995dc..1b26e4f 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -56,6 +56,10 @@ impl RollupDB { .await .unwrap(); } else if let Some(tx) = message.add_processed_transaction { + // LOGIC IS MISSING + + // communication channel with database + // communcation with the frontend } } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index e4f0a1f..bc76d15 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -142,7 +142,7 @@ pub fn run( let result_msg = MessageProcessor::process_message( - &sanitized.unwrap().message(), + &sanitized.unwrap().message(), // ERROR WITH SOLANA_SVM VERSION &vec![], &mut invoke_context, &mut timings, @@ -170,12 +170,33 @@ pub fn run( // let _settle_tx_hash = settle_state("proof".into()).await?; tx_counter = 0u32; + + + // CREATE A PROOF FOR THE CHANGES STATE } } Ok(()) } + +// TWO WAYS -> TRANSACTIONBATCHPROCCESOR OR MESSAGEPROCESSOR + +// PAYTUBE in SVM FOLDER + +// The question of how often to pull/push the state out of mainnet state + +// PDA as a *treasury , to solve problem with sol that could disapear from account + +// to create kind of a program that will lock funds on mainnet + +// MagicBlock relyaing on their infrustructure + +// To make a buffer between sending two transactions + + + + // / In order to use the `TransactionBatchProcessor`, another trait - Solana // / Program Runtime's `ForkGraph` - must be implemented, to tell the batch // / processor how to work across forks. From 887e03b8f8ded90a96e52f1685f422069837622e Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Tue, 4 Feb 2025 09:58:16 +0100 Subject: [PATCH 02/41] add some changes --- .gitignore | 3 ++- rollup_core/Cargo.toml | 1 + rollup_core/src/rollupdb.rs | 22 ++++++++++++++++++++-- rollup_core/src/sequencer.rs | 5 ++++- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index d01bd1a..b9cb9d2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ Cargo.lock # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file +#.idea/ +.DS_Store \ No newline at end of file diff --git a/rollup_core/Cargo.toml b/rollup_core/Cargo.toml index 53a9440..8caac54 100644 --- a/rollup_core/Cargo.toml +++ b/rollup_core/Cargo.toml @@ -15,6 +15,7 @@ solana-sdk = "2.0.7" solana-client = "2.0.7" solana-compute-budget = "2.0.7" solana-bpf-loader-program = "2.0.7" +solana-timings = "2.0.7" env_logger = "0.11.5" log = "0.4.22" anyhow = "1.0.86" diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 1b26e4f..7585cb4 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -1,7 +1,7 @@ use async_channel::{Receiver, Sender}; use serde::{Deserialize, Serialize}; use solana_sdk::{ - account::AccountSharedData, keccak::Hash, pubkey::Pubkey, transaction::Transaction, + account::AccountSharedData, hash::Hash, pubkey::Pubkey, transaction::Transaction, // keccak::Hash -> hash::Hash }; use crossbeam::channel::{Receiver as CBReceiver, Sender as CBSender}; @@ -56,7 +56,25 @@ impl RollupDB { .await .unwrap(); } else if let Some(tx) = message.add_processed_transaction { - // LOGIC IS MISSING + + // unlocking accounts + let locked_keys = tx.message.account_keys.clone(); // get the keys + + // locked_keys.iter().for_each( + // |pubkey| if db.locked_accounts.contains_key(&pubkey) { + // db.locked_accounts.remove(&pubkey); + // } + // ); + + for pubkey in locked_keys { + if let Some(account) = db.locked_accounts.remove(&pubkey) { + db.accounts_db.insert(pubkey, account); // Unlock and restore + } + } + // send transaction to the db.transactions + + db.transactions.insert(tx.message.hash(), tx.clone()); + // communication channel with database // communcation with the frontend diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index bc76d15..87207ab 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -10,13 +10,14 @@ use solana_client::{nonblocking::rpc_client as nonblocking_rpc_client, rpc_clien use solana_compute_budget::compute_budget::ComputeBudget; use solana_program_runtime::{ invoke_context::{self, EnvironmentConfig, InvokeContext}, - loaded_programs::{BlockRelation, ForkGraph, LoadProgramMetrics, ProgramCacheEntry, ProgramCacheForTxBatch, ProgramRuntimeEnvironments}, sysvar_cache, timings::ExecuteTimings, + loaded_programs::{BlockRelation, ForkGraph, LoadProgramMetrics, ProgramCacheEntry, ProgramCacheForTxBatch, ProgramRuntimeEnvironments}, sysvar_cache, }; use solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1; use solana_sdk::{ account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, transaction::{SanitizedTransaction, Transaction}, transaction_context::TransactionContext }; +use solana_timings::ExecuteTimings; use solana_svm::{ message_processor::MessageProcessor, transaction_processing_callback::TransactionProcessingCallback, @@ -143,6 +144,8 @@ pub fn run( let result_msg = MessageProcessor::process_message( &sanitized.unwrap().message(), // ERROR WITH SOLANA_SVM VERSION + // ?should be fixed with help of chagning versions of solana-svm ? + // &sanitized.unwrap().message().to_owned(), &vec![], &mut invoke_context, &mut timings, From 5d7c876dd8fcf055bdf8379f37c6cbfc60d5887b Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Wed, 5 Feb 2025 23:52:54 +0100 Subject: [PATCH 03/41] add some changes --- rollup_core/src/frontend.rs | 2 +- rollup_core/src/main.rs | 1 + rollup_core/src/processor.rs | 112 +++++++++++++++++++++++++++++++++++ rollup_core/src/sequencer.rs | 38 ++++++++++-- 4 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 rollup_core/src/processor.rs diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index c1d0d8d..c3f71c2 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -7,7 +7,7 @@ use actix_web::{error, web, HttpResponse}; use async_channel::{Receiver, Send, Sender}; use crossbeam::channel::{Sender as CBSender, Receiver as CBReceiver}; use serde::{Deserialize, Serialize}; -use solana_sdk::keccak::Hash; +use solana_sdk::hash::Hash; // keccak::Hash use solana_sdk::transaction::Transaction; use crate::rollupdb::RollupDBMessage; diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 8089972..d62a29e 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -11,6 +11,7 @@ mod frontend; mod rollupdb; mod sequencer; mod settle; +mod processor; // #[actix_web::main] fn main() { diff --git a/rollup_core/src/processor.rs b/rollup_core/src/processor.rs new file mode 100644 index 0000000..f802d98 --- /dev/null +++ b/rollup_core/src/processor.rs @@ -0,0 +1,112 @@ +// //! A helper to initialize Solana SVM API's `TransactionBatchProcessor`. + +// use { +// solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1, +// solana_compute_budget::compute_budget::ComputeBudget, +// solana_program_runtime::loaded_programs::{BlockRelation, ForkGraph, ProgramCacheEntry}, +// solana_sdk::{clock::Slot, feature_set::FeatureSet, pubkey::Pubkey, transaction}, +// solana_svm::{ +// account_loader::CheckedTransactionDetails, +// transaction_processing_callback::TransactionProcessingCallback, +// transaction_processor::TransactionBatchProcessor, +// }, +// solana_system_program::system_processor, +// std::sync::{Arc, RwLock}, +// }; + +// /// In order to use the `TransactionBatchProcessor`, another trait - Solana +// /// Program Runtime's `ForkGraph` - must be implemented, to tell the batch +// /// processor how to work across forks. +// /// +// /// Since PayTube doesn't use slots or forks, this implementation is mocked. +// pub(crate) struct PayTubeForkGraph {} + +// impl ForkGraph for PayTubeForkGraph { +// fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation { +// BlockRelation::Unknown +// } +// } + +// /// This function encapsulates some initial setup required to tweak the +// /// `TransactionBatchProcessor` for use within PayTube. +// /// +// /// We're simply configuring the mocked fork graph on the SVM API's program +// /// cache, then adding the System program to the processor's builtins. +// pub(crate) fn create_transaction_batch_processor( +// callbacks: &CB, +// feature_set: &FeatureSet, +// compute_budget: &ComputeBudget, +// fork_graph: Arc>, +// // needed_programs: Vec, +// ) -> TransactionBatchProcessor { +// // Create a new transaction batch processor. +// // +// // We're going to use slot 1 specifically because any programs we add will +// // be deployed in slot 0, and they are delayed visibility until the next +// // slot (1). +// // This includes programs owned by BPF Loader v2, which are automatically +// // marked as "depoyed" in slot 0. +// // See `solana_svm::program_loader::program_with_pubkey` for more +// // details. +// let processor = TransactionBatchProcessor::::new_uninitialized( +// /* slot */ 1, +// /* epoch */ 1, +// // Arc::downgrade(&fork_graph), +// // Some(Arc::new( +// // create_program_runtime_environment_v1(feature_set, compute_budget, false, false) +// // .unwrap(), +// // )), +// // None, +// ); + +// processor.program_cache.write().unwrap().set_fork_graph(Arc::downgrade(&fork_graph)); + +// processor.prepare_program_cache_for_upcoming_feature_set(callbacks, feature_set, compute_budget, 1, 50); + +// // processor.prepare_program_cache_for_upcoming_feature_set(callbacks, upcoming_feature_set, compute_budget, slot_index, slots_in_epoch); + +// // Add the system program builtin. +// processor.add_builtin( +// callbacks, +// solana_system_program::id(), +// "system_program", +// ProgramCacheEntry::new_builtin( +// 0, +// b"system_program".len(), +// system_processor::Entrypoint::vm, +// ), +// ); + +// // Add the BPF Loader v2 builtin, for the SPL Token program. +// processor.add_builtin( +// callbacks, +// solana_sdk::bpf_loader::id(), +// "solana_bpf_loader_program", +// ProgramCacheEntry::new_builtin( +// 0, +// b"solana_bpf_loader_program".len(), +// solana_bpf_loader_program::Entrypoint::vm, +// ), +// ); + +// // Adding any needed programs to the processor. + + +// processor +// } + +// /// This function is also a mock. In the Agave validator, the bank pre-checks +// /// transactions before providing them to the SVM API. We mock this step in +// /// PayTube, since we don't need to perform such pre-checks. +// pub(crate) fn get_transaction_check_results( +// len: usize, +// lamports_per_signature: u64, +// ) -> Vec> { +// vec![ +// transaction::Result::Ok(CheckedTransactionDetails { +// nonce: None, +// lamports_per_signature, +// }); +// len +// ] +// } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 87207ab..34da7b9 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -15,11 +15,12 @@ use solana_program_runtime::{ use solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1; use solana_sdk::{ - account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, transaction::{SanitizedTransaction, Transaction}, transaction_context::TransactionContext + account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, transaction::{SanitizedTransaction, Transaction}, transaction_context::{IndexOfAccount, TransactionContext} }; use solana_timings::ExecuteTimings; use solana_svm::{ message_processor::MessageProcessor, + program_loader::load_program_with_pubkey, transaction_processing_callback::TransactionProcessingCallback, transaction_processor::{TransactionBatchProcessor, TransactionProcessingEnvironment}, }; @@ -53,6 +54,8 @@ pub fn run( let feature_set = FeatureSet::all_enabled(); let fee_structure = FeeStructure::default(); let lamports_per_signature = fee_structure.lamports_per_signature; + + // let rent_collector = RentCollector::default(); // Solana runtime. @@ -71,7 +74,7 @@ pub fn run( let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); - let accounts_data = transaction + let accounts_data = transaction // adding reference .message .account_keys .iter() @@ -83,9 +86,27 @@ pub fn run( }) .collect::>(); + + + let instructions = &transaction.message.instructions; + // let index_array_of_program_pubkeys = Vec::with_capacity(instructions.len()); + let program_ids = &transaction.message.account_keys; + + let needed_programs: Vec<&Pubkey> = instructions + .iter() + .map( + |instruction| + instruction.program_id(program_ids)).collect(); + + + + + let mut transaction_context = TransactionContext::new(accounts_data, Rent::default(), 0, 0); + // here we have to load them somehow + let runtime_env = Arc::new( create_program_runtime_environment_v1(&feature_set, &compute_budget, false, false) .unwrap(), @@ -130,6 +151,8 @@ pub fn run( compute_budget.to_owned() ); + + let mut used_cu = 0u64; let sanitized = SanitizedTransaction::try_from_legacy_transaction( Transaction::from(transaction.clone()), @@ -141,17 +164,22 @@ pub fn run( let mut timings = ExecuteTimings::default(); - + let program_indices: Vec = vec![0]; let result_msg = MessageProcessor::process_message( - &sanitized.unwrap().message(), // ERROR WITH SOLANA_SVM VERSION + &sanitized.unwrap().message().to_owned(), // ERROR WITH SOLANA_SVM VERSION // ?should be fixed with help of chagning versions of solana-svm ? // &sanitized.unwrap().message().to_owned(), - &vec![], + &[program_indices], // TODO: automotize this process &mut invoke_context, &mut timings, &mut used_cu, ); + log::info!("{:?}", &result_msg); + log::info!("The message was done sucessfully"); + + + // Send processed transaction to db for storage and availability rollupdb_sender .send(RollupDBMessage { From 0aea08bb26e059426e58b4561dc23a77867bd8a5 Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Wed, 5 Feb 2025 23:56:05 +0100 Subject: [PATCH 04/41] pew --- rollup_core/src/sequencer.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 34da7b9..cd5f20a 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -88,6 +88,7 @@ pub fn run( + //****************************************************************************************************/ let instructions = &transaction.message.instructions; // let index_array_of_program_pubkeys = Vec::with_capacity(instructions.len()); let program_ids = &transaction.message.account_keys; @@ -97,10 +98,7 @@ pub fn run( .map( |instruction| instruction.program_id(program_ids)).collect(); - - - - + //****************************************************************************************************/ let mut transaction_context = TransactionContext::new(accounts_data, Rent::default(), 0, 0); @@ -152,6 +150,16 @@ pub fn run( ); + // HAS TO BE AN ADDRESS OF THE PROGRAM + let key = Pubkey::new_unique(); + + let program_cache_entry = load_program_with_pubkey( + // TODO: add arguments + ); + + invoke_context.program_cache_for_tx_batch.replenish(key, program_cache_entry.unwrap()); + + let mut used_cu = 0u64; let sanitized = SanitizedTransaction::try_from_legacy_transaction( From 62e40122c36688bfeda25cbfcce6b58e8009b1de Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Fri, 7 Feb 2025 16:23:32 +0100 Subject: [PATCH 05/41] add tests and some work --- .gitignore | 3 ++- rollup_client/mykey_1.json | 1 + rollup_client/src/main.rs | 6 +++--- rollup_client/testkey.json | 1 + rollup_core/src/sequencer.rs | 22 +++++++++++++++++----- rollup_core/src/settle.rs | 2 +- 6 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 rollup_client/mykey_1.json create mode 100644 rollup_client/testkey.json diff --git a/.gitignore b/.gitignore index b9cb9d2..8f16da7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ Cargo.lock # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -.DS_Store \ No newline at end of file +.DS_Store +rollup_client/.json \ No newline at end of file diff --git a/rollup_client/mykey_1.json b/rollup_client/mykey_1.json new file mode 100644 index 0000000..3daab42 --- /dev/null +++ b/rollup_client/mykey_1.json @@ -0,0 +1 @@ +[85,229,25,2,195,236,148,200,234,152,150,129,92,99,246,193,19,117,254,211,33,38,1,243,126,75,180,191,140,17,12,140,29,55,69,35,33,158,92,165,33,100,170,203,216,15,80,53,180,40,104,87,171,226,158,188,1,197,92,158,205,28,79,20] \ No newline at end of file diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index cd819bc..ad9c839 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -28,12 +28,12 @@ pub struct GetTransaction { #[tokio::main] async fn main() -> Result<()> { - let keypair = signer::keypair::read_keypair_file("/home/dev/.solana/testkey.json").unwrap(); - let keypair2 = signer::keypair::read_keypair_file("/home/dev/.solana/mykey_1.json").unwrap(); + let keypair = signer::keypair::read_keypair_file("/Users/nicknut/Desktop/Q1_SVM/Rollup_SVM_Q1/Basic_Rollup_fork/rollup_client/mykey_1.json").unwrap(); + let keypair2 = signer::keypair::read_keypair_file("/Users/nicknut/Desktop/Q1_SVM/Rollup_SVM_Q1/Basic_Rollup_fork/rollup_client/testkey.json").unwrap(); let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); let ix = - system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), 1 * LAMPORTS_PER_SOL); + system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), 1 * (LAMPORTS_PER_SOL/2)); let tx = Transaction::new_signed_with_payer( &[ix], Some(&keypair2.pubkey()), diff --git a/rollup_client/testkey.json b/rollup_client/testkey.json new file mode 100644 index 0000000..3738a4f --- /dev/null +++ b/rollup_client/testkey.json @@ -0,0 +1 @@ +[104,117,96,190,131,134,101,169,18,12,134,152,145,31,0,55,246,209,135,166,162,8,183,14,233,140,170,45,220,131,115,20,87,120,117,245,178,151,16,46,66,46,115,100,12,254,35,169,37,124,73,61,124,55,178,158,70,21,128,162,115,99,69,220] \ No newline at end of file diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index cd5f20a..9b7c9e7 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -86,7 +86,17 @@ pub fn run( }) .collect::>(); + + + // let needed_programs = &accounts_data.iter().map(|(pubkey, account)| { + // match account.executable() { + // true => (pubkey, account), + // false => (), + // } + // }).collect::>(); + // log::info!("accounts_data: {needed_programs:?}"); + //****************************************************************************************************/ let instructions = &transaction.message.instructions; @@ -100,7 +110,7 @@ pub fn run( instruction.program_id(program_ids)).collect(); //****************************************************************************************************/ - let mut transaction_context = TransactionContext::new(accounts_data, Rent::default(), 0, 0); + let mut transaction_context = TransactionContext::new(accounts_data, Rent::default(), 1, 100); // here we have to load them somehow @@ -120,6 +130,8 @@ pub fn run( Epoch::default(), ); + // prog_cache.replenish(accounts_data., entry) + let sysvar_c = sysvar_cache::SysvarCache::default(); let env = EnvironmentConfig::new( Hash::default(), @@ -153,16 +165,16 @@ pub fn run( // HAS TO BE AN ADDRESS OF THE PROGRAM let key = Pubkey::new_unique(); - let program_cache_entry = load_program_with_pubkey( + // let program_cache_entry = load_program_with_pubkey( // TODO: add arguments - ); + // ); - invoke_context.program_cache_for_tx_batch.replenish(key, program_cache_entry.unwrap()); + // invoke_context.program_cache_for_tx_batch.replenish(key, program_cache_entry.unwrap()); let mut used_cu = 0u64; - let sanitized = SanitizedTransaction::try_from_legacy_transaction( + let sanitized = SanitizedTransaction::try_from_legacy_transaction( // to check here for the problem Transaction::from(transaction.clone()), &HashSet::new(), ) diff --git a/rollup_core/src/settle.rs b/rollup_core/src/settle.rs index bf5dab3..e16d7d8 100644 --- a/rollup_core/src/settle.rs +++ b/rollup_core/src/settle.rs @@ -1,6 +1,6 @@ use anyhow::Result; use solana_client::nonblocking::rpc_client::RpcClient; -use solana_sdk::{blake3::Hash, transaction::Transaction}; +use solana_sdk::{hash::Hash, transaction::Transaction}; // Settle the state on solana, called by sequencer pub async fn settle_state(proof: Hash) -> Result { From b26efdd90813069305a59ce2c104194067dc0129 Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Sat, 8 Feb 2025 19:15:42 +0100 Subject: [PATCH 06/41] change logic from message processor to transaction batch processor --- rollup_core/Cargo.toml | 1 + rollup_core/src/loader.rs | 62 +++++++ rollup_core/src/main.rs | 1 + rollup_core/src/processor.rs | 198 ++++++++++----------- rollup_core/src/sequencer.rs | 321 ++++++++++++++++++++++++----------- 5 files changed, 383 insertions(+), 200 deletions(-) create mode 100644 rollup_core/src/loader.rs diff --git a/rollup_core/Cargo.toml b/rollup_core/Cargo.toml index 8caac54..da4aaa2 100644 --- a/rollup_core/Cargo.toml +++ b/rollup_core/Cargo.toml @@ -16,6 +16,7 @@ solana-client = "2.0.7" solana-compute-budget = "2.0.7" solana-bpf-loader-program = "2.0.7" solana-timings = "2.0.7" +solana-system-program = "2.0.7" env_logger = "0.11.5" log = "0.4.22" anyhow = "1.0.86" diff --git a/rollup_core/src/loader.rs b/rollup_core/src/loader.rs new file mode 100644 index 0000000..d21e719 --- /dev/null +++ b/rollup_core/src/loader.rs @@ -0,0 +1,62 @@ +//! PayTube's "account loader" component, which provides the SVM API with the +//! ability to load accounts for PayTube channels. +//! +//! The account loader is a simple example of an RPC client that can first load +//! an account from the base chain, then cache it locally within the protocol +//! for the duration of the channel. + +use { + solana_client::rpc_client::RpcClient, + solana_sdk::{ + account::{AccountSharedData, ReadableAccount}, + pubkey::Pubkey, + }, + solana_svm::transaction_processing_callback::TransactionProcessingCallback, + std::{collections::HashMap, sync::RwLock}, +}; + +/// An account loading mechanism to hoist accounts from the base chain up to +/// an active PayTube channel. +/// +/// Employs a simple cache mechanism to ensure accounts are only loaded once. +pub struct RollupAccountLoader<'a> { + pub cache: RwLock>, + pub rpc_client: &'a RpcClient, +} + +impl<'a> RollupAccountLoader<'a> { + pub fn new(rpc_client: &'a RpcClient) -> Self { + Self { + cache: RwLock::new(HashMap::new()), + rpc_client, + } + } + + pub fn add_account(&mut self, pubkey: Pubkey, account: AccountSharedData) { + self.cache.write().unwrap().insert(pubkey, account); + } +} + +/// Implementation of the SVM API's `TransactionProcessingCallback` interface. +/// +/// The SVM API requires this plugin be provided to provide the SVM with the +/// ability to load accounts. +/// +/// In the Agave validator, this implementation is Bank, powered by AccountsDB. +impl TransactionProcessingCallback for RollupAccountLoader<'_> { + fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option { + if let Some(account) = self.cache.read().unwrap().get(pubkey) { + return Some(account.clone()); + } + + let account: AccountSharedData = self.rpc_client.get_account(pubkey).ok()?.into(); + self.cache.write().unwrap().insert(*pubkey, account.clone()); + + Some(account) + } + + fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option { + self.get_account_shared_data(account) + .and_then(|account| owners.iter().position(|key| account.owner().eq(key))) + } +} diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index d62a29e..3dee5b1 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -12,6 +12,7 @@ mod rollupdb; mod sequencer; mod settle; mod processor; +mod loader; // #[actix_web::main] fn main() { diff --git a/rollup_core/src/processor.rs b/rollup_core/src/processor.rs index f802d98..90e2264 100644 --- a/rollup_core/src/processor.rs +++ b/rollup_core/src/processor.rs @@ -1,112 +1,112 @@ -// //! A helper to initialize Solana SVM API's `TransactionBatchProcessor`. +//! A helper to initialize Solana SVM API's `TransactionBatchProcessor`. -// use { -// solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1, -// solana_compute_budget::compute_budget::ComputeBudget, -// solana_program_runtime::loaded_programs::{BlockRelation, ForkGraph, ProgramCacheEntry}, -// solana_sdk::{clock::Slot, feature_set::FeatureSet, pubkey::Pubkey, transaction}, -// solana_svm::{ -// account_loader::CheckedTransactionDetails, -// transaction_processing_callback::TransactionProcessingCallback, -// transaction_processor::TransactionBatchProcessor, -// }, -// solana_system_program::system_processor, -// std::sync::{Arc, RwLock}, -// }; +use { + solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1, + solana_compute_budget::compute_budget::ComputeBudget, + solana_program_runtime::loaded_programs::{BlockRelation, ForkGraph, ProgramCacheEntry}, + solana_sdk::{clock::Slot, feature_set::FeatureSet, pubkey::Pubkey, transaction}, + solana_svm::{ + account_loader::CheckedTransactionDetails, + transaction_processing_callback::TransactionProcessingCallback, + transaction_processor::TransactionBatchProcessor, + }, + solana_system_program::system_processor, + std::sync::{Arc, RwLock}, +}; -// /// In order to use the `TransactionBatchProcessor`, another trait - Solana -// /// Program Runtime's `ForkGraph` - must be implemented, to tell the batch -// /// processor how to work across forks. -// /// -// /// Since PayTube doesn't use slots or forks, this implementation is mocked. -// pub(crate) struct PayTubeForkGraph {} +/// In order to use the `TransactionBatchProcessor`, another trait - Solana +/// Program Runtime's `ForkGraph` - must be implemented, to tell the batch +/// processor how to work across forks. +/// +/// Since PayTube doesn't use slots or forks, this implementation is mocked. +pub(crate) struct RollupForkGraph {} -// impl ForkGraph for PayTubeForkGraph { -// fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation { -// BlockRelation::Unknown -// } -// } +impl ForkGraph for RollupForkGraph { + fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation { + BlockRelation::Unknown + } +} -// /// This function encapsulates some initial setup required to tweak the -// /// `TransactionBatchProcessor` for use within PayTube. -// /// -// /// We're simply configuring the mocked fork graph on the SVM API's program -// /// cache, then adding the System program to the processor's builtins. -// pub(crate) fn create_transaction_batch_processor( -// callbacks: &CB, -// feature_set: &FeatureSet, -// compute_budget: &ComputeBudget, -// fork_graph: Arc>, -// // needed_programs: Vec, -// ) -> TransactionBatchProcessor { -// // Create a new transaction batch processor. -// // -// // We're going to use slot 1 specifically because any programs we add will -// // be deployed in slot 0, and they are delayed visibility until the next -// // slot (1). -// // This includes programs owned by BPF Loader v2, which are automatically -// // marked as "depoyed" in slot 0. -// // See `solana_svm::program_loader::program_with_pubkey` for more -// // details. -// let processor = TransactionBatchProcessor::::new_uninitialized( -// /* slot */ 1, -// /* epoch */ 1, -// // Arc::downgrade(&fork_graph), -// // Some(Arc::new( -// // create_program_runtime_environment_v1(feature_set, compute_budget, false, false) -// // .unwrap(), -// // )), -// // None, -// ); +/// This function encapsulates some initial setup required to tweak the +/// `TransactionBatchProcessor` for use within PayTube. +/// +/// We're simply configuring the mocked fork graph on the SVM API's program +/// cache, then adding the System program to the processor's builtins. +pub(crate) fn create_transaction_batch_processor( + callbacks: &CB, + feature_set: &FeatureSet, + compute_budget: &ComputeBudget, + fork_graph: Arc>, + // needed_programs: Vec, +) -> TransactionBatchProcessor { + // Create a new transaction batch processor. + // + // We're going to use slot 1 specifically because any programs we add will + // be deployed in slot 0, and they are delayed visibility until the next + // slot (1). + // This includes programs owned by BPF Loader v2, which are automatically + // marked as "depoyed" in slot 0. + // See `solana_svm::program_loader::program_with_pubkey` for more + // details. + let processor = TransactionBatchProcessor::::new_uninitialized( + /* slot */ 1, + /* epoch */ 1, + // Arc::downgrade(&fork_graph), + // Some(Arc::new( + // create_program_runtime_environment_v1(feature_set, compute_budget, false, false) + // .unwrap(), + // )), + // None, + ); -// processor.program_cache.write().unwrap().set_fork_graph(Arc::downgrade(&fork_graph)); + processor.program_cache.write().unwrap().set_fork_graph(Arc::downgrade(&fork_graph)); -// processor.prepare_program_cache_for_upcoming_feature_set(callbacks, feature_set, compute_budget, 1, 50); + processor.prepare_program_cache_for_upcoming_feature_set(callbacks, feature_set, compute_budget, 1, 50); -// // processor.prepare_program_cache_for_upcoming_feature_set(callbacks, upcoming_feature_set, compute_budget, slot_index, slots_in_epoch); + // processor.prepare_program_cache_for_upcoming_feature_set(callbacks, upcoming_feature_set, compute_budget, slot_index, slots_in_epoch); -// // Add the system program builtin. -// processor.add_builtin( -// callbacks, -// solana_system_program::id(), -// "system_program", -// ProgramCacheEntry::new_builtin( -// 0, -// b"system_program".len(), -// system_processor::Entrypoint::vm, -// ), -// ); + // Add the system program builtin. + processor.add_builtin( + callbacks, + solana_system_program::id(), + "system_program", + ProgramCacheEntry::new_builtin( + 0, + b"system_program".len(), + system_processor::Entrypoint::vm, + ), + ); -// // Add the BPF Loader v2 builtin, for the SPL Token program. -// processor.add_builtin( -// callbacks, -// solana_sdk::bpf_loader::id(), -// "solana_bpf_loader_program", -// ProgramCacheEntry::new_builtin( -// 0, -// b"solana_bpf_loader_program".len(), -// solana_bpf_loader_program::Entrypoint::vm, -// ), -// ); + // Add the BPF Loader v2 builtin, for the SPL Token program. + processor.add_builtin( + callbacks, + solana_sdk::bpf_loader::id(), + "solana_bpf_loader_program", + ProgramCacheEntry::new_builtin( + 0, + b"solana_bpf_loader_program".len(), + solana_bpf_loader_program::Entrypoint::vm, + ), + ); -// // Adding any needed programs to the processor. + // Adding any needed programs to the processor. -// processor -// } + processor +} -// /// This function is also a mock. In the Agave validator, the bank pre-checks -// /// transactions before providing them to the SVM API. We mock this step in -// /// PayTube, since we don't need to perform such pre-checks. -// pub(crate) fn get_transaction_check_results( -// len: usize, -// lamports_per_signature: u64, -// ) -> Vec> { -// vec![ -// transaction::Result::Ok(CheckedTransactionDetails { -// nonce: None, -// lamports_per_signature, -// }); -// len -// ] -// } +/// This function is also a mock. In the Agave validator, the bank pre-checks +/// transactions before providing them to the SVM API. We mock this step in +/// PayTube, since we don't need to perform such pre-checks. +pub(crate) fn get_transaction_check_results( + len: usize, + lamports_per_signature: u64, +) -> Vec> { + vec![ + transaction::Result::Ok(CheckedTransactionDetails { + nonce: None, + lamports_per_signature, + }); + len + ] +} diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 9b7c9e7..8abc295 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -1,3 +1,4 @@ +use core::panic; use std::{ collections::{HashMap, HashSet}, sync::{Arc, RwLock}, @@ -15,17 +16,19 @@ use solana_program_runtime::{ use solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1; use solana_sdk::{ - account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, transaction::{SanitizedTransaction, Transaction}, transaction_context::{IndexOfAccount, TransactionContext} + account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, instruction, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, sysvar::instructions, transaction::{SanitizedTransaction, Transaction}, transaction_context::{IndexOfAccount, TransactionContext}, }; use solana_timings::ExecuteTimings; use solana_svm::{ message_processor::MessageProcessor, program_loader::load_program_with_pubkey, transaction_processing_callback::TransactionProcessingCallback, - transaction_processor::{TransactionBatchProcessor, TransactionProcessingEnvironment}, + transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment}, }; use crate::{rollupdb::RollupDBMessage, settle::settle_state}; +use crate::loader::RollupAccountLoader; +use crate::processor::*; pub fn run( sequencer_receiver_channel: CBReceiver, @@ -54,6 +57,10 @@ pub fn run( let feature_set = FeatureSet::all_enabled(); let fee_structure = FeeStructure::default(); let lamports_per_signature = fee_structure.lamports_per_signature; + let rent_collector = RentCollector::default(); + let mut timings = ExecuteTimings::default(); + let fork_graph = Arc::new(RwLock::new(RollupForkGraph {})); + // let rent_collector = RentCollector::default(); @@ -86,121 +93,69 @@ pub fn run( }) .collect::>(); - - - // let needed_programs = &accounts_data.iter().map(|(pubkey, account)| { - // match account.executable() { - // true => (pubkey, account), - // false => (), - // } - // }).collect::>(); - - // log::info!("accounts_data: {needed_programs:?}"); - - - //****************************************************************************************************/ - let instructions = &transaction.message.instructions; - // let index_array_of_program_pubkeys = Vec::with_capacity(instructions.len()); - let program_ids = &transaction.message.account_keys; - - let needed_programs: Vec<&Pubkey> = instructions - .iter() - .map( - |instruction| - instruction.program_id(program_ids)).collect(); - //****************************************************************************************************/ + let mut used_cu = 0u64; + let sanitized = SanitizedTransaction::try_from_legacy_transaction( // to check here for the problem + Transaction::from(transaction.clone()), + &HashSet::new(), + ); - let mut transaction_context = TransactionContext::new(accounts_data, Rent::default(), 1, 100); + log::info!("{:?}", sanitized.clone()); + let needed_programs: Vec<(Pubkey, AccountSharedData)> = + accounts_data + .iter() + .filter(|(pubkey, account)| account.executable()) + .map(|(pubkey, account)| (pubkey.clone(), account.clone())) + .collect(); - // here we have to load them somehow + log::info!("accounts_data: {needed_programs:?}"); - let runtime_env = Arc::new( - create_program_runtime_environment_v1(&feature_set, &compute_budget, false, false) - .unwrap(), + let mut rollup_account_loader = RollupAccountLoader::new( + &rpc_client_temp, ); - let mut prog_cache = ProgramCacheForTxBatch::new( - Slot::default(), - ProgramRuntimeEnvironments { - program_runtime_v1: runtime_env.clone(), - program_runtime_v2: runtime_env, - }, - None, - Epoch::default(), - ); + for (pubkey, account) in needed_programs.iter() { + rollup_account_loader.add_account(*pubkey, account.clone()); + } - // prog_cache.replenish(accounts_data., entry) - let sysvar_c = sysvar_cache::SysvarCache::default(); - let env = EnvironmentConfig::new( - Hash::default(), - None, - None, - Arc::new(feature_set), - lamports_per_signature, - &sysvar_c, - ); - // let default_env = EnvironmentConfig::new(blockhash, epoch_total_stake, epoch_vote_accounts, feature_set, lamports_per_signature, sysvar_cache) - - // let processing_environment = TransactionProcessingEnvironment { - // blockhash: Hash::default(), - // epoch_total_stake: None, - // epoch_vote_accounts: None, - // feature_set: Arc::new(feature_set), - // fee_structure: Some(&fee_structure), - // lamports_per_signature, - // rent_collector: Some(&rent_collector), - // }; - - let mut invoke_context = InvokeContext::new( - &mut transaction_context, - &mut prog_cache, - env, - None, - compute_budget.to_owned() + let processor = create_transaction_batch_processor( + &rollup_account_loader, + &feature_set, + &compute_budget, + Arc::clone(&fork_graph), ); + let checks = get_transaction_check_results(1, fee_structure.lamports_per_signature); + let sanitized_transaction = &[sanitized.unwrap()]; - // HAS TO BE AN ADDRESS OF THE PROGRAM - let key = Pubkey::new_unique(); + let processing_environment = TransactionProcessingEnvironment { + blockhash: Hash::default(), + epoch_total_stake: None, + epoch_vote_accounts: None, + feature_set: Arc::new(feature_set), + fee_structure: Some(&fee_structure), + lamports_per_signature: fee_structure.lamports_per_signature, + rent_collector: Some(&rent_collector), + }; - // let program_cache_entry = load_program_with_pubkey( - // TODO: add arguments - // ); + let processing_config = TransactionProcessingConfig { + compute_budget: Some(compute_budget), + ..Default::default() + }; - // invoke_context.program_cache_for_tx_batch.replenish(key, program_cache_entry.unwrap()); - - let mut used_cu = 0u64; - let sanitized = SanitizedTransaction::try_from_legacy_transaction( // to check here for the problem - Transaction::from(transaction.clone()), - &HashSet::new(), - ) - ; - log::info!("{:?}", sanitized.clone()); - - - let mut timings = ExecuteTimings::default(); - - let program_indices: Vec = vec![0]; - let result_msg = MessageProcessor::process_message( - &sanitized.unwrap().message().to_owned(), // ERROR WITH SOLANA_SVM VERSION - // ?should be fixed with help of chagning versions of solana-svm ? - // &sanitized.unwrap().message().to_owned(), - &[program_indices], // TODO: automotize this process - &mut invoke_context, - &mut timings, - &mut used_cu, + let status = processor.load_and_execute_sanitized_transactions( + &rollup_account_loader, + sanitized_transaction, + checks, + &processing_environment, + &processing_config ); - - log::info!("{:?}", &result_msg); - log::info!("The message was done sucessfully"); - - - - // Send processed transaction to db for storage and availability + log::info!("{:#?}", status.processing_results); + + // Send processed transaction to db for storage and availability rollupdb_sender .send(RollupDBMessage { lock_accounts: None, @@ -231,6 +186,170 @@ pub fn run( } + + + + // //****************************************************************************************************/ + // // let instructions = &transaction.message.instructions; + // // // let index_array_of_program_pubkeys = Vec::with_capacity(instructions.len()); + // // let program_ids = &transaction.message.account_keys; + + // // let needed_programs: Vec<&Pubkey> = instructions + // // .iter() + // // .map( + // // |instruction| + // // instruction.program_id(program_ids)).collect(); + // //****************************************************************************************************/ + + // let mut transaction_context = TransactionContext::new( + // accounts_data, + // Rent::default(), + // compute_budget.max_instruction_stack_depth, + // compute_budget.max_instruction_trace_length, + // ); + // // transaction_context.get_current_instruction_context().unwrap().get_index_of_program_account_in_transaction(2).unwrap(); + // // transaction_context.push(); + + + // // here we have to load them somehow + + // let runtime_env = Arc::new( + // create_program_runtime_environment_v1(&feature_set, &compute_budget, false, false) + // .unwrap(), + // ); + + // let mut prog_cache = ProgramCacheForTxBatch::new( + // Slot::default(), + // ProgramRuntimeEnvironments { + // program_runtime_v1: runtime_env.clone(), + // program_runtime_v2: runtime_env, + // }, + // None, + // Epoch::default(), + // ); + + + // // prog_cache.replenish(accounts_data., entry) + + // let sysvar_c = sysvar_cache::SysvarCache::default(); + // let env = EnvironmentConfig::new( + // Hash::default(), + // None, + // None, + // Arc::new(feature_set), + // lamports_per_signature, + // &sysvar_c, + // ); + // // let default_env = EnvironmentConfig::new(blockhash, epoch_total_stake, epoch_vote_accounts, feature_set, lamports_per_signature, sysvar_cache) + + // // let processing_environment = TransactionProcessingEnvironment { + // // blockhash: Hash::default(), + // // epoch_total_stake: None, + // // epoch_vote_accounts: None, + // // feature_set: Arc::new(feature_set), + // // fee_structure: Some(&fee_structure), + // // lamports_per_signature, + // // rent_collector: Some(&rent_collector), + // // }; + + + + // // for (pubkey, account) in rollup_account_loader.cache.read().unwrap().iter() { + // // let _p = rollup_account_loader.get_account_shared_data(pubkey); + // // log::info!("account: {_p:?}"); + // // } + // // let cache = &rollup_account_loader.cache.read().unwrap(); + // // let pew = cache.keys().next().cloned().unwrap(); + // // let owner = cache.get(&pew).unwrap().owner(); + // // log::debug!("pubkey: {owner:?}"); + + + // let program_cache_entry = load_program_with_pubkey( + // &rollup_account_loader, + // &prog_cache.environments, + // &rollup_account_loader.cache.read().unwrap().keys().next().cloned().unwrap(),//&needed_programs[0].0, + // 0, + // &mut ExecuteTimings::default(), + // false + // ); + + // log::info!("program_cache_entry: {program_cache_entry:?}"); + + // prog_cache.replenish( + // needed_programs[0].0, + // program_cache_entry.unwrap(), + // ); + // // { + // // let instruction_ctx = transaction_context.get_current_instruction_context(); + // // log::debug!("instruction_ctx: {instruction_ctx:?}"); + // // } + // // let instruction_ctx_height = transaction_context.get_instruction_context_stack_height(); + + // // log::debug!("instruction_ctx_height: {instruction_ctx_height}"); + + // // let instruction_ctx_next = transaction_context.get_next_instruction_context(); + // // // let instruction_ctx = transaction_context.get_next_instruction_context(); + + // // log::debug!("instruction_ctx: {instruction_ctx_next:?}"); + + + + // let mut invoke_context = InvokeContext::new( + // &mut transaction_context, + // &mut prog_cache, + // env, + // None, + // compute_budget.to_owned() + // ); + + + // // let instruction_ctx_2 = invoke_context.transaction_context.get_current_instruction_context(); + // // log::debug!("instruction_ctx_2: {instruction_ctx_2:?}"); + // // let instruction_ctx_height = invoke_context.transaction_context.get_instruction_context_stack_height(); + // // log::debug!("instruction_ctx_height: {instruction_ctx_height}"); + // // let instruction_ctx_height = invoke_context.transaction_context.get_instruction_context_at_index_in_trace(0); + // // log::debug!("instruction_ctx_height: {instruction_ctx_height:?}"); + + + + + // // HAS TO BE AN ADDRESS OF THE PROGRAM + + // // invoke_context.program_cache_for_tx_batch.replenish(key, program_cache_entry.unwrap()); + + + + + + + + // // let account_index = invoke_context + // // .transaction_context + // // .find_index_of_account(&instructions::id()); + + // // if account_index.is_none() { + // // panic!("Could not find instructions account"); + // // } + + // let program_indices: Vec = vec![0]; + // let result_msg = MessageProcessor::process_message( + // &sanitized.unwrap().message().to_owned(), // ERROR WITH SOLANA_SVM VERSION + // // ?should be fixed with help of chagning versions of solana-svm ? + // // &sanitized.unwrap().message().to_owned(), + // &[program_indices], // TODO: automotize this process + // &mut invoke_context, + // &mut timings, + // &mut used_cu, + // ); + + // log::info!("{:?}", &result_msg); + // log::info!("The message was done sucessfully"); + + + + + + // TWO WAYS -> TRANSACTIONBATCHPROCCESOR OR MESSAGEPROCESSOR // PAYTUBE in SVM FOLDER From dfb17f9045935e65ecb2c136c2c8eb8129a0bf2b Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Tue, 11 Feb 2025 16:14:40 +0100 Subject: [PATCH 07/41] add some testing changes --- rollup_core/src/frontend.rs | 1 + rollup_core/src/main.rs | 7 +++--- rollup_core/src/rollupdb.rs | 18 ++++++++++++++++ rollup_core/src/sequencer.rs | 42 +++++++++++++++++++++++++++++------- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index c3f71c2..232fcf7 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -67,6 +67,7 @@ pub async fn get_transaction( add_processed_transaction: None, frontend_get_tx: Some(Hash::new(body.get_tx.as_bytes())), add_settle_proof: None, + get_account: None, }) .await .unwrap(); diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 3dee5b1..164c7b6 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -4,7 +4,7 @@ use actix_web::{web, App, HttpServer}; use async_channel; use frontend::FrontendMessage; use rollupdb::{RollupDB, RollupDBMessage}; -use solana_sdk::transaction::Transaction; +use solana_sdk::{account::AccountSharedData, transaction::Transaction}; use tokio::runtime::Builder; use crossbeam; mod frontend; @@ -27,6 +27,7 @@ fn main() { // let (sequencer_sender, sequencer_receiver) = async_channel::bounded::(100); // Channel for communication between frontend and sequencer // let (rollupdb_sender, rollupdb_receiver) = async_channel::unbounded::(); // Channel for communication between sequencer and accountsdb let (frontend_sender, frontend_receiver) = async_channel::unbounded::(); // Channel for communication between data availability layer and frontend + let (account_sender, account_receiver) = async_channel::unbounded::>(); // std::thread::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); // let rt = Builder::new() @@ -46,8 +47,8 @@ fn main() { - rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2).unwrap()}); - rt.spawn(RollupDB::run(rollupdb_receiver, fe_2)); + rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).unwrap()}); + rt.spawn(RollupDB::run(rollupdb_receiver, fe_2, account_sender)); }); // Create sequencer task // tokio::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 7585cb4..0570153 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -18,6 +18,7 @@ pub struct RollupDBMessage { pub add_processed_transaction: Option, pub frontend_get_tx: Option, pub add_settle_proof: Option, + pub get_account: Option, } #[derive(Serialize, Debug, Default)] @@ -31,6 +32,7 @@ impl RollupDB { pub async fn run( rollup_db_receiver: CBReceiver, frontend_sender: Sender, + account_sender: Sender> ) { let mut db = RollupDB { accounts_db: HashMap::new(), @@ -39,8 +41,14 @@ impl RollupDB { }; while let Ok(message) = rollup_db_receiver.recv() { + println!("everything is allllllllllllllllllllrighty"); + println!("everything is allllllllllllllllllllrighty"); + println!("everything is allllllllllllllllllllrighty"); if let Some(accounts_to_lock) = message.lock_accounts { // Lock accounts, by removing them from the accounts_db hashmap, and adding them to locked accounts + log::info!("{:#?}", db.locked_accounts); + log::info!("looooooooooooooooooooooooooooooging"); + println!("everything is allllllllllllllllllllrighty"); let _ = accounts_to_lock.iter().map(|pubkey| { db.locked_accounts .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()) @@ -79,6 +87,16 @@ impl RollupDB { // communication channel with database // communcation with the frontend } + else if let Some(pubkey) = message.get_account { + log::info!("{:#?}", db.locked_accounts); + log::info!("looooooooooooooooooooooooooooooging"); + if let Some(account) = db.locked_accounts.get(&pubkey) { + account_sender.send(Some(account.clone())).await.unwrap(); + } + else { + account_sender.send(None).await.unwrap(); + } + } } } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 8abc295..3918cc5 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -5,7 +5,7 @@ use std::{ }; use anyhow::{anyhow, Result}; -use async_channel::Sender; +use async_channel::{Sender, Receiver}; use crossbeam::channel::{Sender as CBSender, Receiver as CBReceiver}; use solana_client::{nonblocking::rpc_client as nonblocking_rpc_client, rpc_client::RpcClient}; use solana_compute_budget::compute_budget::ComputeBudget; @@ -33,6 +33,7 @@ use crate::processor::*; pub fn run( sequencer_receiver_channel: CBReceiver, rollupdb_sender: CBSender, + account_reciever: Receiver> ) -> Result<()> { let mut tx_counter = 0u32; while let transaction = sequencer_receiver_channel.recv().unwrap() { @@ -45,6 +46,7 @@ pub fn run( frontend_get_tx: None, add_settle_proof: None, add_processed_transaction: None, + get_account: None, }) .map_err(|_| anyhow!("failed to send message to rollupdb"))?; @@ -85,13 +87,36 @@ pub fn run( .message .account_keys .iter() - .map(|pubkey| { - ( - pubkey.clone(), - rpc_client_temp.get_account(pubkey).unwrap().into(), - ) + .filter_map(|pubkey| { + rollupdb_sender.send(RollupDBMessage { + lock_accounts: None, + frontend_get_tx: None, + add_settle_proof: None, + add_processed_transaction: None, + get_account: Some(*pubkey), + }) + .map_err(|_| anyhow!("failed to send message to rollupdb")).unwrap(); + + if let Ok(Some(account_data)) = account_reciever.try_recv() { + // If the pubkey exists in accounts_db, return the stored value + Some((pubkey.clone(), account_data.clone())) + } else { + // Otherwise, fetch from rpc_client_temp + log::info!("Fetching account from rpc_client_temp"); + log::info!("{:?}", pubkey); + match rpc_client_temp.get_account(pubkey) { + Ok(account) => Some((pubkey.clone(), account.into())), + Err(_) => None, // If the fetch fails, filter it out // SHOULD RETURN A CUSTOM ERROR + } + } }) .collect::>(); + // .map(|pubkey| { + // ( + // pubkey.clone(), + // rpc_client_temp.get_account(pubkey).unwrap().into(), + // ) + // }) let mut used_cu = 0u64; let sanitized = SanitizedTransaction::try_from_legacy_transaction( // to check here for the problem @@ -108,7 +133,7 @@ pub fn run( .map(|(pubkey, account)| (pubkey.clone(), account.clone())) .collect(); - log::info!("accounts_data: {needed_programs:?}"); + // log::info!("accounts_data: {needed_programs:?}"); let mut rollup_account_loader = RollupAccountLoader::new( &rpc_client_temp, @@ -154,7 +179,7 @@ pub fn run( &processing_config ); log::info!("{:#?}", status.processing_results); - + // Send processed transaction to db for storage and availability rollupdb_sender .send(RollupDBMessage { @@ -162,6 +187,7 @@ pub fn run( add_processed_transaction: Some(transaction), frontend_get_tx: None, add_settle_proof: None, + get_account: None, }) .unwrap(); From 0e805b0cc25188a7587ff4fb3e5162eeaef45904 Mon Sep 17 00:00:00 2001 From: NkamaWilliams Date: Wed, 12 Feb 2025 19:57:45 +0100 Subject: [PATCH 08/41] added bundler --untested --- rollup_client/src/main.rs | 13 +++--- rollup_core/Cargo.toml | 3 +- rollup_core/src/bundler.rs | 87 +++++++++++++++++++++++++++++++++++++ rollup_core/src/frontend.rs | 14 +++--- rollup_core/src/main.rs | 3 +- rollup_core/src/rollupdb.rs | 6 ++- 6 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 rollup_core/src/bundler.rs diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index ad9c839..204b1e5 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -12,6 +12,7 @@ use solana_sdk::{ transaction::Transaction, }; use solana_transaction_status::UiTransactionEncoding::{self, Binary}; +use core::hash; use std::{collections::HashMap, str::FromStr}; // use serde_json; @@ -28,8 +29,10 @@ pub struct GetTransaction { #[tokio::main] async fn main() -> Result<()> { - let keypair = signer::keypair::read_keypair_file("/Users/nicknut/Desktop/Q1_SVM/Rollup_SVM_Q1/Basic_Rollup_fork/rollup_client/mykey_1.json").unwrap(); - let keypair2 = signer::keypair::read_keypair_file("/Users/nicknut/Desktop/Q1_SVM/Rollup_SVM_Q1/Basic_Rollup_fork/rollup_client/testkey.json").unwrap(); + let path = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/mykey_1.json"; + let path2 = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/testkey.json"; + let keypair = signer::keypair::read_keypair_file(path.to_string()).unwrap(); + let keypair2 = signer::keypair::read_keypair_file(path2.to_string()).unwrap(); let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); let ix = @@ -79,11 +82,11 @@ async fn main() -> Result<()> { let tx_resp = client .post("http://127.0.0.1:8080/get_transaction") - .json(&HashMap::from([("get_tx", hasher.result().to_string())])) + .json(&GetTransaction{get_tx: rtx.sol_transaction.message.hash().to_string()}) .send() - .await? - .json::>() .await?; + // .json::>() + // .await?; println!("{tx_resp:#?}"); diff --git a/rollup_core/Cargo.toml b/rollup_core/Cargo.toml index da4aaa2..abd6209 100644 --- a/rollup_core/Cargo.toml +++ b/rollup_core/Cargo.toml @@ -21,4 +21,5 @@ env_logger = "0.11.5" log = "0.4.22" anyhow = "1.0.86" crossbeam = "0.8.4" -async-channel = "2.3.1" \ No newline at end of file +async-channel = "2.3.1" +bincode = "1.3.3" diff --git a/rollup_core/src/bundler.rs b/rollup_core/src/bundler.rs new file mode 100644 index 0000000..9f573a2 --- /dev/null +++ b/rollup_core/src/bundler.rs @@ -0,0 +1,87 @@ +use std::collections::HashMap; + +use solana_sdk::{instruction::{CompiledInstruction, Instruction}, pubkey::Pubkey, system_instruction::{self, SystemInstruction}, system_program, transaction::Transaction}; +use bincode::deserialize; + +pub fn get_transaction_instructions(tx: &Transaction) -> Vec{ + tx.message.instructions.clone() +} + +pub fn is_transfer_ix(cix: &CompiledInstruction, account_keys: &[Pubkey]) -> bool { + if cix.program_id_index as usize >= account_keys.len(){ + return false; + } + let program_id = account_keys[cix.program_id_index as usize]; + if program_id != system_program::ID{ + return false + } + + matches!( + deserialize::(&cix.data), + Ok(SystemInstruction::Transfer { .. }) + ) +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +struct TBundlerKey { + keys: [Pubkey; 2], +} + +pub struct TransferBundler { + transfers: HashMap, +} + +impl TransferBundler { + pub fn new() -> Self { + Self { + transfers: HashMap::new() + } + } + + pub fn parse_instruction(ix: &CompiledInstruction, account_keys: &[Pubkey]) -> Option<(Pubkey, Pubkey, i128)>{ + //Ensure the instruction is from System Program (where transfer is from) + if ix.program_id_index as usize >= account_keys.len() || account_keys[ix.program_id_index as usize] != system_program::ID{ + return None; + } + //Ensure we have at least 2 accounts for transfer and enough data for SOL amount + if ix.accounts.len() < 2 || ix.data.len() < 8 { + return None; + } + + //Get accounts involved in transfer and amount to be transferred + let from = account_keys[ix.accounts[0] as usize]; + let to = account_keys[ix.accounts[1] as usize]; + + log::info!("IX DATA: {:?}", ix.data); + + let amount = u64::from_le_bytes(ix.data[4..12].try_into().ok()?); + Some((from, to, amount as i128)) + } + + //Parses transactions and add transfer ixs to TransferBundler + pub fn bundle(&mut self, transaction: Transaction){ + let ixs = get_transaction_instructions(&transaction); + let account_keys: &[Pubkey] = &transaction.message.account_keys; + for ix in ixs { + if is_transfer_ix(&ix, account_keys){ + let (from, to, amount) = Self::parse_instruction(&ix, account_keys).unwrap(); + let mut keys = [from, to]; + keys.sort(); + + *self.transfers.entry(TBundlerKey {keys}).or_default() += if from == keys[0] {amount} else {-amount}; + } + } + } + + pub fn generate_final(self) -> Vec { + self.transfers.into_iter().filter_map(|(map_key, val)| { + if val < 0 { + Some(system_instruction::transfer(&map_key.keys[1], &map_key.keys[0], val.unsigned_abs() as u64)) + } else if val > 0 { + Some(system_instruction::transfer(&map_key.keys[0], &map_key.keys[1], val as u64)) + } else { + None + } + }).collect() + } +} \ No newline at end of file diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index c3f71c2..6754ed4 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -53,11 +53,12 @@ pub async fn submit_transaction( pub async fn get_transaction( body: web::Json, - sequencer_sender: web::Data>, + // sequencer_sender: web::Data>, rollupdb_sender: web::Data>, frontend_receiver: web::Data>, ) -> actix_web::Result { // Validate transaction structure with serialization in function signature + println!("Getting tx..."); log::info!("Requested transaction"); log::info!("{body:?}"); @@ -72,11 +73,12 @@ pub async fn get_transaction( .unwrap(); if let Ok(frontend_message) = frontend_receiver.recv().await { - return Ok(HttpResponse::Ok().json(RollupTransaction { - sender: "Rollup RPC".into(), - sol_transaction: frontend_message.transaction.unwrap(), - })); - // Ok(HttpResponse::Ok().json(HashMap::from([("Transaction status", "requested")]))) + // return Ok(HttpResponse::Ok().json(RollupTransaction { + // sender: "Rollup RPC".into(), + // sol_transaction: frontend_message.transaction.unwrap(), + // })); + log::info!("Requested TX:\n {:?}", frontend_message.transaction.unwrap()); + return Ok(HttpResponse::Ok().json(HashMap::from([("Requested transaction status", "gotten successfully")]))); } Ok(HttpResponse::Ok().json(HashMap::from([("Transaction status", "requested")]))) diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 3dee5b1..a8381a1 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -13,6 +13,7 @@ mod sequencer; mod settle; mod processor; mod loader; +mod bundler; // #[actix_web::main] fn main() { @@ -46,8 +47,8 @@ fn main() { - rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2).unwrap()}); rt.spawn(RollupDB::run(rollupdb_receiver, fe_2)); + rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2).unwrap()}); }); // Create sequencer task // tokio::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 7585cb4..7c4540d 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -39,6 +39,7 @@ impl RollupDB { }; while let Ok(message) = rollup_db_receiver.recv() { + log::info!("Received RollupDBMessage"); if let Some(accounts_to_lock) = message.lock_accounts { // Lock accounts, by removing them from the accounts_db hashmap, and adding them to locked accounts let _ = accounts_to_lock.iter().map(|pubkey| { @@ -46,6 +47,7 @@ impl RollupDB { .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()) }); } else if let Some(get_this_hash_tx) = message.frontend_get_tx { + log::info!("Getting tx for frontend"); let req_tx = db.transactions.get(&get_this_hash_tx).unwrap(); frontend_sender @@ -56,7 +58,7 @@ impl RollupDB { .await .unwrap(); } else if let Some(tx) = message.add_processed_transaction { - + log::info!("Adding processed tx"); // unlocking accounts let locked_keys = tx.message.account_keys.clone(); // get the keys @@ -74,7 +76,7 @@ impl RollupDB { // send transaction to the db.transactions db.transactions.insert(tx.message.hash(), tx.clone()); - + log::info!("PROCESSED TX: {}", db.transactions.len()); // communication channel with database // communcation with the frontend From fc00f5507fe44178b5aa8a868a3ede6221a977e6 Mon Sep 17 00:00:00 2001 From: NkamaWilliams Date: Wed, 12 Feb 2025 20:57:14 +0100 Subject: [PATCH 09/41] small changes for testing purposes --- rollup_client/src/main.rs | 89 ++++++++++++++++++++++++++---------- rollup_core/src/bundler.rs | 2 + rollup_core/src/frontend.rs | 1 + rollup_core/src/rollupdb.rs | 12 +++++ rollup_core/src/sequencer.rs | 11 +++++ 5 files changed, 92 insertions(+), 23 deletions(-) diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index 204b1e5..5f29090 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -66,29 +66,72 @@ async fn main() -> Result<()> { // let serialized_rollup_transaction = serde_json::to_string(&rtx)?; - let submit_transaction = client - .post("http://127.0.0.1:8080/submit_transaction") - .json(&rtx) - .send() - .await?; - // .json() - // .await?; - - println!("{submit_transaction:#?}"); - let mut hasher = Hasher::default(); - hasher.hash(bincode::serialize(&rtx.sol_transaction).unwrap().as_slice()); - - println!("{:#?}", hasher.clone().result()); - - let tx_resp = client - .post("http://127.0.0.1:8080/get_transaction") - .json(&GetTransaction{get_tx: rtx.sol_transaction.message.hash().to_string()}) - .send() - .await?; - // .json::>() - // .await?; - - println!("{tx_resp:#?}"); + //UNCOMMENT + // let submit_transaction = client + // .post("http://127.0.0.1:8080/submit_transaction") + // .json(&rtx) + // .send() + // .await?; + // // .json() + // // .await?; + + // println!("{submit_transaction:#?}"); + // let mut hasher = Hasher::default(); + // hasher.hash(bincode::serialize(&rtx.sol_transaction).unwrap().as_slice()); + + // println!("{:#?}", hasher.clone().result()); + + // let tx_resp = client + // .post("http://127.0.0.1:8080/get_transaction") + // .json(&GetTransaction{get_tx: rtx.sol_transaction.message.hash().to_string()}) + // .send() + // .await?; + // // .json::>() + // // .await?; + + // println!("{tx_resp:#?}"); + + let amounts: Vec = vec![4, -2, 3, -5, 1, -4, 2, -1, 3, -4]; + let mut txs: Vec = vec![]; + for amt in amounts { + if amt > 0 { + txs.push(gen_transfer_tx(path.to_string(), path2.to_string(), amt as u64).await); + } else { + txs.push(gen_transfer_tx(path2.to_string(), path.to_string(), amt.abs() as u64).await); + } + } + + for tx in txs { + let rtx = RollupTransaction { + sender: "Me".into(), + sol_transaction: tx + }; + + let submission = client + .post("http://127.0.0.1:8080/submit_transaction") + .json(&rtx) + .send() + .await?; + + println!("Submission {submission:#?}"); + } Ok(()) } + +async fn gen_transfer_tx(path1: String, path2: String, amount: u64) -> Transaction { + let path = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/mykey_1.json"; + let path2 = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/testkey.json"; + let keypair = signer::keypair::read_keypair_file(path.to_string()).unwrap(); + let keypair2 = signer::keypair::read_keypair_file(path2.to_string()).unwrap(); + let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); + + let ix = + system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), (amount/10) * LAMPORTS_PER_SOL); + Transaction::new_signed_with_payer( + &[ix], + Some(&keypair2.pubkey()), + &[&keypair2], + rpc_client.get_latest_blockhash().await.unwrap(), + ) +} \ No newline at end of file diff --git a/rollup_core/src/bundler.rs b/rollup_core/src/bundler.rs index 9f573a2..a00bdea 100644 --- a/rollup_core/src/bundler.rs +++ b/rollup_core/src/bundler.rs @@ -52,6 +52,8 @@ impl TransferBundler { let from = account_keys[ix.accounts[0] as usize]; let to = account_keys[ix.accounts[1] as usize]; + log::info!("FROM: {:?}", from.to_string()); + log::info!("TO: {:?}", to.to_string()); log::info!("IX DATA: {:?}", ix.data); let amount = u64::from_le_bytes(ix.data[4..12].try_into().ok()?); diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index 6754ed4..7e2a270 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -68,6 +68,7 @@ pub async fn get_transaction( add_processed_transaction: None, frontend_get_tx: Some(Hash::new(body.get_tx.as_bytes())), add_settle_proof: None, + bundle_tx: false }) .await .unwrap(); diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 7c4540d..076bf7c 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -1,4 +1,5 @@ use async_channel::{Receiver, Sender}; +use log::log; use serde::{Deserialize, Serialize}; use solana_sdk::{ account::AccountSharedData, hash::Hash, pubkey::Pubkey, transaction::Transaction, // keccak::Hash -> hash::Hash @@ -11,6 +12,7 @@ use std::{ }; use crate::frontend::FrontendMessage; +use crate::bundler::*; #[derive(Serialize, Deserialize)] pub struct RollupDBMessage { @@ -18,6 +20,8 @@ pub struct RollupDBMessage { pub add_processed_transaction: Option, pub frontend_get_tx: Option, pub add_settle_proof: Option, + //Testing purposes + pub bundle_tx: bool } #[derive(Serialize, Debug, Default)] @@ -80,6 +84,14 @@ impl RollupDB { // communication channel with database // communcation with the frontend + } else if message.bundle_tx { + log::info!("BUNDLING TX"); + let mut tx_bundler = TransferBundler::new(); + for (_, tx) in db.transactions.clone() { + tx_bundler.bundle(tx); + } + let final_ix = tx_bundler.generate_final(); + log::info!("Final Transfer Ix: {final_ix:#?}"); } } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 8abc295..dee94af 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -45,6 +45,7 @@ pub fn run( frontend_get_tx: None, add_settle_proof: None, add_processed_transaction: None, + bundle_tx: false }) .map_err(|_| anyhow!("failed to send message to rollupdb"))?; @@ -162,12 +163,22 @@ pub fn run( add_processed_transaction: Some(transaction), frontend_get_tx: None, add_settle_proof: None, + bundle_tx: false }) .unwrap(); // Call settle if transaction amount since last settle hits 10 if tx_counter >= 10 { + //bundle transfer tx test + rollupdb_sender.send(RollupDBMessage { + lock_accounts: None, + add_processed_transaction: None, + add_settle_proof: None, + frontend_get_tx: None, + bundle_tx: true + }).unwrap(); + // Lock db to avoid state changes during settlement // Prepare root hash, or your own proof to send to chain From f398e44db5a5c98898805e3f974ad0b7d7d807a2 Mon Sep 17 00:00:00 2001 From: NkamaWilliams Date: Thu, 13 Feb 2025 15:52:52 +0100 Subject: [PATCH 10/41] tx bundler v0.1 --- rollup_client/src/main.rs | 9 ++++----- rollup_core/src/rollupdb.rs | 1 + rollup_core/src/sequencer.rs | 16 +++++++++++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index 5f29090..2642374 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -13,7 +13,7 @@ use solana_sdk::{ }; use solana_transaction_status::UiTransactionEncoding::{self, Binary}; use core::hash; -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, ops::Div, str::FromStr}; // use serde_json; #[derive(Serialize, Deserialize, Debug)] @@ -120,14 +120,13 @@ async fn main() -> Result<()> { } async fn gen_transfer_tx(path1: String, path2: String, amount: u64) -> Transaction { - let path = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/mykey_1.json"; - let path2 = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/testkey.json"; - let keypair = signer::keypair::read_keypair_file(path.to_string()).unwrap(); + println!("Amount: {amount}"); + let keypair = signer::keypair::read_keypair_file(path1.to_string()).unwrap(); let keypair2 = signer::keypair::read_keypair_file(path2.to_string()).unwrap(); let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); let ix = - system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), (amount/10) * LAMPORTS_PER_SOL); + system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), amount * (LAMPORTS_PER_SOL / 10)); Transaction::new_signed_with_payer( &[ix], Some(&keypair2.pubkey()), diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 076bf7c..10fe8eb 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -92,6 +92,7 @@ impl RollupDB { } let final_ix = tx_bundler.generate_final(); log::info!("Final Transfer Ix: {final_ix:#?}"); + db.transactions.clear(); } } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index dee94af..75dc531 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -29,6 +29,7 @@ use solana_svm::{ use crate::{rollupdb::RollupDBMessage, settle::settle_state}; use crate::loader::RollupAccountLoader; use crate::processor::*; +use crate::bundler::*; pub fn run( sequencer_receiver_channel: CBReceiver, @@ -160,7 +161,7 @@ pub fn run( rollupdb_sender .send(RollupDBMessage { lock_accounts: None, - add_processed_transaction: Some(transaction), + add_processed_transaction: Some(transaction.clone()), frontend_get_tx: None, add_settle_proof: None, bundle_tx: false @@ -168,6 +169,19 @@ pub fn run( .unwrap(); + //View sent processed tx details + let ixs = get_transaction_instructions(&transaction); + let acc_keys: &[Pubkey] = &transaction.message.account_keys; + if let Some((from, to, amount)) = TransferBundler::parse_instruction(&ixs[0], acc_keys) { + log::info!(" + Transaction Info\n + From: {from:?}\n + To: {to:?}\n + Amount: {amount} + + ") + } + // Call settle if transaction amount since last settle hits 10 if tx_counter >= 10 { //bundle transfer tx test From 23be72865fb1958379ddd59676468b4582982019 Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:59:04 +0100 Subject: [PATCH 11/41] adding connection to the locking, unlocking, processing... in the rollup_db and loader --- rollup_client/src/main.rs | 8 +- rollup_core/src/errors.rs | 3 + rollup_core/src/frontend.rs | 1 + rollup_core/src/loader.rs | 84 ++++++++++++++++--- rollup_core/src/main.rs | 20 +++-- rollup_core/src/rollupdb.rs | 109 +++++++++++++++++++------ rollup_core/src/sequencer.rs | 153 +++++++++++++++++++++-------------- 7 files changed, 274 insertions(+), 104 deletions(-) create mode 100644 rollup_core/src/errors.rs diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index ad9c839..467b400 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use solana_client::nonblocking::rpc_client::{self, RpcClient}; use solana_sdk::{ instruction::Instruction, - keccak::{Hash, Hasher}, + hash::{Hash, Hasher}, native_token::LAMPORTS_PER_SOL, signature::Signature, signer::{self, Signer}, @@ -33,7 +33,7 @@ async fn main() -> Result<()> { let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); let ix = - system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), 1 * (LAMPORTS_PER_SOL/2)); + system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), 1 * (LAMPORTS_PER_SOL/4)); let tx = Transaction::new_signed_with_payer( &[ix], Some(&keypair2.pubkey()), @@ -81,9 +81,9 @@ async fn main() -> Result<()> { .post("http://127.0.0.1:8080/get_transaction") .json(&HashMap::from([("get_tx", hasher.result().to_string())])) .send() - .await? - .json::>() .await?; + // .json::>() + // .await?; println!("{tx_resp:#?}"); diff --git a/rollup_core/src/errors.rs b/rollup_core/src/errors.rs new file mode 100644 index 0000000..56f9752 --- /dev/null +++ b/rollup_core/src/errors.rs @@ -0,0 +1,3 @@ +pub enum RollupErrors { + TransactionFailedOnlyFeesWereCollected, +} diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index 232fcf7..1c7f7fc 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -64,6 +64,7 @@ pub async fn get_transaction( rollupdb_sender .send(RollupDBMessage { lock_accounts: None, + add_new_data: None, add_processed_transaction: None, frontend_get_tx: Some(Hash::new(body.get_tx.as_bytes())), add_settle_proof: None, diff --git a/rollup_core/src/loader.rs b/rollup_core/src/loader.rs index d21e719..19b6848 100644 --- a/rollup_core/src/loader.rs +++ b/rollup_core/src/loader.rs @@ -6,15 +6,18 @@ //! for the duration of the channel. use { - solana_client::rpc_client::RpcClient, - solana_sdk::{ + log::info, solana_client::rpc_client::RpcClient, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, pubkey::Pubkey, - }, - solana_svm::transaction_processing_callback::TransactionProcessingCallback, - std::{collections::HashMap, sync::RwLock}, + }, solana_svm::transaction_processing_callback::TransactionProcessingCallback, std::{collections::HashMap, sync::RwLock}, + std::ops::IndexMut, }; +// impl IndexMut for HashMap { +// fn index_mut(&mut self, index: Idx) -> &mut Self::Output { + +// } +// } /// An account loading mechanism to hoist accounts from the base chain up to /// an active PayTube channel. /// @@ -32,8 +35,63 @@ impl<'a> RollupAccountLoader<'a> { } } - pub fn add_account(&mut self, pubkey: Pubkey, account: AccountSharedData) { - self.cache.write().unwrap().insert(pubkey, account); + pub fn add_account(&mut self, pubkey: Pubkey, modified_or_new_account: AccountSharedData) { + let mut map = self.cache.write().unwrap(); + let res = map.contains_key(&pubkey); + if res == false { + map.insert(pubkey, modified_or_new_account); + log::info!("newone: {:?}", map); + } else { // PROBLEM HERE!!! SOMEHOW DON'T FIND THIS ELSE + + map.insert(pubkey, modified_or_new_account); + // map.entry(pubkey) + // .and_modify(|account| { + // log::info!("Updating existing account."); + // *account = modified_or_new_account.clone(); // Replace existing account + // }); + log::info!("oldone: {:?}", map); + + } + // let mut cache = self.cache.write().unwrap(); // Get a write lock once + + // if let Some(account) = cache.get_mut(&pubkey) { + // log::info!("Updating existing account"); + // *account = modified_or_new_account; // Overwrite existing entry + // } else { + // log::info!("Adding new account"); + // cache.insert(pubkey, modified_or_new_account); // Insert new entry + // } + + + // let mut cache = self.cache.write().unwrap(); + + + // cache.entry(pubkey) + // .and_modify(|account| { + // log::info!("Updating existing account."); + // *account = modified_or_new_account.clone(); // Replace existing account + // }) + // .or_insert_with(|| { + // log::info!("Inserting new account."); + // modified_or_new_account + // }); + // if let Some(account) = self.cache.read().unwrap().get(&pubkey) { + // log::info!("it is alright"); + // cache.entry(pubkey).and_modify( + // // |account| { + // // let data = account.data(); + // // account.set_data_from_slice(&data) + // // } + // |account| *account = modified_or_new_account.clone() + // ); + // } + // else { + // self.cache.write().unwrap().insert(pubkey, modified_or_new_account); + // log::info!("fucking problem"); + // } + // log::info!("cache: {:?}", self.cache.read().unwrap()); + // log::info!("entryyy: {:?}", self.cache.read().unwrap().get(&pubkey)); + } } @@ -46,13 +104,17 @@ impl<'a> RollupAccountLoader<'a> { impl TransactionProcessingCallback for RollupAccountLoader<'_> { fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option { if let Some(account) = self.cache.read().unwrap().get(pubkey) { + log::info!("the account was loaded from the database"); return Some(account.clone()); } + else { + None + } - let account: AccountSharedData = self.rpc_client.get_account(pubkey).ok()?.into(); - self.cache.write().unwrap().insert(*pubkey, account.clone()); - - Some(account) + // let account: AccountSharedData = self.rpc_client.get_account(pubkey).ok()?.into(); + // self.cache.write().unwrap().insert(*pubkey, account.clone()); + // log::info!("the account was loaded from the rpcclient"); + // Some(account) } fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option { diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 164c7b6..50dbc83 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -6,6 +6,7 @@ use frontend::FrontendMessage; use rollupdb::{RollupDB, RollupDBMessage}; use solana_sdk::{account::AccountSharedData, transaction::Transaction}; use tokio::runtime::Builder; +use tokio::sync::oneshot; use crossbeam; mod frontend; mod rollupdb; @@ -13,16 +14,20 @@ mod sequencer; mod settle; mod processor; mod loader; +mod errors; // #[actix_web::main] -fn main() { +// #[tokio::main] +fn main() { // async env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug")); log::info!("starting HTTP server at http://localhost:8080"); - + // let (tx, rx) = oneshot::channel::>(); let (sequencer_sender, sequencer_receiver) = crossbeam::channel::unbounded::(); let (rollupdb_sender, rollupdb_receiver) = crossbeam::channel::unbounded::(); +// let (sequencer_sender, sequencer_receiver) = async_channel::bounded::(100); +// let (rollupdb_sender, rollupdb_receiver) = async_channel::unbounded::(); // let (sequencer_sender, sequencer_receiver) = async_channel::bounded::(100); // Channel for communication between frontend and sequencer // let (rollupdb_sender, rollupdb_receiver) = async_channel::unbounded::(); // Channel for communication between sequencer and accountsdb @@ -46,9 +51,14 @@ fn main() { .unwrap(); - - rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).unwrap()}); - rt.spawn(RollupDB::run(rollupdb_receiver, fe_2, account_sender)); + rt.spawn(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).unwrap()}); // .unwrap() + // rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).unwrap()}); + rt.block_on(RollupDB::run(rollupdb_receiver, fe_2, account_sender)); + // rt.block_on(async { + // tokio::spawn(async move { + // RollupDB::run(rollupdb_receiver, fe_2, account_sender).await; + // }); + // }); }); // Create sequencer task // tokio::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 0570153..176eea0 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -1,5 +1,6 @@ use async_channel::{Receiver, Sender}; use serde::{Deserialize, Serialize}; +use solana_client::rpc_client::RpcClient; use solana_sdk::{ account::AccountSharedData, hash::Hash, pubkey::Pubkey, transaction::Transaction, // keccak::Hash -> hash::Hash }; @@ -9,16 +10,18 @@ use std::{ collections::{HashMap, HashSet}, default, }; - +use tokio::sync::oneshot; use crate::frontend::FrontendMessage; #[derive(Serialize, Deserialize)] pub struct RollupDBMessage { pub lock_accounts: Option>, pub add_processed_transaction: Option, + pub add_new_data: Option>, pub frontend_get_tx: Option, pub add_settle_proof: Option, pub get_account: Option, + // pub response: Option, } #[derive(Serialize, Debug, Default)] @@ -26,6 +29,7 @@ pub struct RollupDB { accounts_db: HashMap, locked_accounts: HashMap, transactions: HashMap, + // async_ver_recv: Receiver> } impl RollupDB { @@ -39,20 +43,30 @@ impl RollupDB { locked_accounts: HashMap::new(), transactions: HashMap::new(), }; - while let Ok(message) = rollup_db_receiver.recv() { - println!("everything is allllllllllllllllllllrighty"); - println!("everything is allllllllllllllllllllrighty"); - println!("everything is allllllllllllllllllllrighty"); if let Some(accounts_to_lock) = message.lock_accounts { + log::info!("locking: {:?}", db.accounts_db); // Lock accounts, by removing them from the accounts_db hashmap, and adding them to locked accounts - log::info!("{:#?}", db.locked_accounts); - log::info!("looooooooooooooooooooooooooooooging"); - println!("everything is allllllllllllllllllllrighty"); - let _ = accounts_to_lock.iter().map(|pubkey| { - db.locked_accounts - .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()) - }); + for pubkey in accounts_to_lock.iter() { + if let Some(account) = db.accounts_db.get(pubkey) { + db.locked_accounts + .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()); + log::info!("account was found"); + } + else { + let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); + let account = rpc_client_temp.get_account(pubkey).unwrap(); + let data: AccountSharedData = account.into(); + db.locked_accounts + .insert(pubkey.clone(), data); + log::info!("account was not found"); + } + } + log::info!("locking done: {:?}", db.accounts_db); + log::info!("locked accounts done: {:?}", db.locked_accounts); + + + // log::info!("2: {:#?}", db.locked_accounts); } else if let Some(get_this_hash_tx) = message.frontend_get_tx { let req_tx = db.transactions.get(&get_this_hash_tx).unwrap(); @@ -65,31 +79,32 @@ impl RollupDB { .unwrap(); } else if let Some(tx) = message.add_processed_transaction { + let processed_data = message.add_new_data.unwrap(); + // unlocking accounts let locked_keys = tx.message.account_keys.clone(); // get the keys + log::info!("it is starting accounts_db{:#?}", db.accounts_db); + log::info!("it is starting locked_db{:#?}", db.locked_accounts); + for (pubkey, data) in processed_data.iter() { + db.locked_accounts.remove(pubkey).unwrap(); + db.accounts_db.insert(*pubkey, data.clone()); + log::info!("it is final accounts_db{:#?}", db.accounts_db); + log::info!("it is final locked_db{:#?}", db.locked_accounts); + - // locked_keys.iter().for_each( - // |pubkey| if db.locked_accounts.contains_key(&pubkey) { - // db.locked_accounts.remove(&pubkey); - // } - // ); - - for pubkey in locked_keys { - if let Some(account) = db.locked_accounts.remove(&pubkey) { - db.accounts_db.insert(pubkey, account); // Unlock and restore - } } // send transaction to the db.transactions db.transactions.insert(tx.message.hash(), tx.clone()); - + log::info!("locked: {:#?}", db.locked_accounts); + log::info!("43210: {:#?}", db.accounts_db); // communication channel with database // communcation with the frontend } else if let Some(pubkey) = message.get_account { - log::info!("{:#?}", db.locked_accounts); - log::info!("looooooooooooooooooooooooooooooging"); + + log::info!("4321: {:#?}", db.locked_accounts); if let Some(account) = db.locked_accounts.get(&pubkey) { account_sender.send(Some(account.clone())).await.unwrap(); } @@ -100,3 +115,47 @@ impl RollupDB { } } } + + + + // if let Some(account) = db.locked_accounts.remove(&pubkey) { + // let data_for_the_account = data.get(pubkey).unwrap() + // db.accounts_db.insert(pubkey, data.get(pubkey).unwrap()); // Unlock and restore + // } +// accounts_to_lock + // .iter() + // .map(|pubkey| { + // // match db.locked_accounts.get(pubkey) { + // // Some(account) => { + // // db.locked_accounts + // // .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()); + // // } + // // None => { + // // let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); + // // let account = rpc_client_temp.get_account(pubkey).unwrap(); + // // let data: AccountSharedData = account.into(); + // // db.locked_accounts + // // .insert(pubkey.clone(), data); + // // } + // // } + // log::info!("99999999999999999999999999999999"); + // if let Some(account) = db.accounts_db.get(pubkey) { + // db.locked_accounts + // .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()); + // log::info!("111111111111111111111112"); + // } + // else { + // let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); + // let account = rpc_client_temp.get_account(pubkey).unwrap(); + // let data: AccountSharedData = account.into(); + // db.locked_accounts + // .insert(pubkey.clone(), data); + // log::info!("2222222222222222222222222"); + // } + // }); + + + // let _ = accounts_to_lock.iter().map(|pubkey| { + // db.locked_accounts + // .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()) + // }); diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 3918cc5..6f62791 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -20,22 +20,29 @@ use solana_sdk::{ }; use solana_timings::ExecuteTimings; use solana_svm::{ - message_processor::MessageProcessor, - program_loader::load_program_with_pubkey, - transaction_processing_callback::TransactionProcessingCallback, - transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment}, + message_processor::MessageProcessor, program_loader::load_program_with_pubkey, transaction_processing_callback::TransactionProcessingCallback, transaction_processing_result::ProcessedTransaction, transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment} }; - +use tokio; use crate::{rollupdb::RollupDBMessage, settle::settle_state}; use crate::loader::RollupAccountLoader; use crate::processor::*; +use crate::errors::RollupErrors; -pub fn run( - sequencer_receiver_channel: CBReceiver, - rollupdb_sender: CBSender, - account_reciever: Receiver> +pub fn run( // async + sequencer_receiver_channel: CBReceiver, // CBReceiver + rollupdb_sender: CBSender, // CBSender + account_reciever: Receiver>, + // rx: tokio::sync::oneshot::Receiver> // sync_ver_sender ) -> Result<()> { + // let (tx, rx) = oneshot::channel(); // Create a channel to wait for response + let mut tx_counter = 0u32; + + let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); + + let mut rollup_account_loader = RollupAccountLoader::new( + &rpc_client_temp, + ); while let transaction = sequencer_receiver_channel.recv().unwrap() { let accounts_to_lock = transaction.message.account_keys.clone(); tx_counter += 1; @@ -45,44 +52,24 @@ pub fn run( lock_accounts: Some(accounts_to_lock), frontend_get_tx: None, add_settle_proof: None, + add_new_data: None, add_processed_transaction: None, get_account: None, + // response: Some(true), }) .map_err(|_| anyhow!("failed to send message to rollupdb"))?; - - // Verify ransaction signatures, integrity - - // Process transaction + // rx.await.map_err(|_| anyhow!("failed to receive response"))?; let compute_budget = ComputeBudget::default(); let feature_set = FeatureSet::all_enabled(); - let fee_structure = FeeStructure::default(); + let mut fee_structure = FeeStructure::default(); + fee_structure.lamports_per_signature = 0; let lamports_per_signature = fee_structure.lamports_per_signature; let rent_collector = RentCollector::default(); let mut timings = ExecuteTimings::default(); let fork_graph = Arc::new(RwLock::new(RollupForkGraph {})); - - - // let rent_collector = RentCollector::default(); - - // Solana runtime. - // let fork_graph = Arc::new(RwLock::new(SequencerForkGraph {})); - - // // create transaction processor, add accounts and programs, builtins, - // let processor = TransactionBatchProcessor::::default(); - - // let mut cache = processor.program_cache.write().unwrap(); - - // // Initialize the mocked fork graph. - // // let fork_graph = Arc::new(RwLock::new(PayTubeForkGraph {})); - // cache.fork_graph = Some(Arc::downgrade(&fork_graph)); - - // let rent = Rent::default(); - - let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); - let accounts_data = transaction // adding reference .message .account_keys @@ -92,18 +79,24 @@ pub fn run( lock_accounts: None, frontend_get_tx: None, add_settle_proof: None, + add_new_data: None, add_processed_transaction: None, get_account: Some(*pubkey), + // response: Some(true) }) .map_err(|_| anyhow!("failed to send message to rollupdb")).unwrap(); - + // rx.await.map_err(|_| anyhow!("failed to receive response"))?; if let Ok(Some(account_data)) = account_reciever.try_recv() { // If the pubkey exists in accounts_db, return the stored value + log::info!("account was recieved"); + log::info!("account was recieved, its data: {:?}", account_data); Some((pubkey.clone(), account_data.clone())) } else { + log::info!("account was copied from rpc"); + log::info!("account was copied from rpc, its data: {:?}", rpc_client_temp.get_account(pubkey).unwrap()); // Otherwise, fetch from rpc_client_temp - log::info!("Fetching account from rpc_client_temp"); - log::info!("{:?}", pubkey); + // log::info!("Fetching account from rpc_client_temp"); + // log::info!("{:?}", pubkey); match rpc_client_temp.get_account(pubkey) { Ok(account) => Some((pubkey.clone(), account.into())), Err(_) => None, // If the fetch fails, filter it out // SHOULD RETURN A CUSTOM ERROR @@ -111,12 +104,7 @@ pub fn run( } }) .collect::>(); - // .map(|pubkey| { - // ( - // pubkey.clone(), - // rpc_client_temp.get_account(pubkey).unwrap().into(), - // ) - // }) + log::info!("accounts_data: {:?}", &accounts_data); let mut used_cu = 0u64; let sanitized = SanitizedTransaction::try_from_legacy_transaction( // to check here for the problem @@ -133,15 +121,12 @@ pub fn run( .map(|(pubkey, account)| (pubkey.clone(), account.clone())) .collect(); - // log::info!("accounts_data: {needed_programs:?}"); - - let mut rollup_account_loader = RollupAccountLoader::new( - &rpc_client_temp, - ); - - for (pubkey, account) in needed_programs.iter() { + + log::info!("mmmnnn: {:?}", accounts_data); + for (pubkey, account) in accounts_data.iter() { rollup_account_loader.add_account(*pubkey, account.clone()); } + log::info!("huylo: {:?}", rollup_account_loader.cache.read().unwrap()); let processor = create_transaction_batch_processor( @@ -169,8 +154,6 @@ pub fn run( ..Default::default() }; - - let status = processor.load_and_execute_sanitized_transactions( &rollup_account_loader, sanitized_transaction, @@ -179,12 +162,37 @@ pub fn run( &processing_config ); log::info!("{:#?}", status.processing_results); + log::info!("error_metrics: {:#?}", status.error_metrics); + let data_new = + status + .processing_results + .iter() + .map(|res| { + println!("Executed transaction:"); + log::info!("Executed transaction"); + let enum_one = res.as_ref().unwrap(); + + match enum_one { + ProcessedTransaction::Executed(tx) => { + println!("Executed transaction: {:?}", tx.loaded_transaction.accounts); + Some(tx.loaded_transaction.accounts.clone()) + } + ProcessedTransaction::FeesOnly(tx) => { + println!("Fees-only transaction: {:?}", tx); + None + } + } + }).collect::>>>(); + + let first_index_data = data_new[0].as_ref().unwrap().clone(); + log::info!("swq {:?}", first_index_data); // Send processed transaction to db for storage and availability rollupdb_sender .send(RollupDBMessage { lock_accounts: None, add_processed_transaction: Some(transaction), + add_new_data: Some(first_index_data), frontend_get_tx: None, add_settle_proof: None, get_account: None, @@ -194,24 +202,51 @@ pub fn run( // Call settle if transaction amount since last settle hits 10 if tx_counter >= 10 { - // Lock db to avoid state changes during settlement + tx_counter = 0u32; + } + } + Ok(()) +} + + + // CREATE A PROOF FOR THE CHANGES STATE + +// Lock db to avoid state changes during settlement // Prepare root hash, or your own proof to send to chain // Send proof to chain // let _settle_tx_hash = settle_state("proof".into()).await?; - tx_counter = 0u32; + // .map(|pubkey| { + // ( + // pubkey.clone(), + // rpc_client_temp.get_account(pubkey).unwrap().into(), + // ) + // }) - // CREATE A PROOF FOR THE CHANGES STATE - } - } +// log::info!("accounts_data: {needed_programs:?}"); - Ok(()) -} + // for (pubkey, account) in needed_programs.iter() { + // rollup_account_loader.add_account(*pubkey, account.clone()); + // } + +// let rent_collector = RentCollector::default(); + + // Solana runtime. + // let fork_graph = Arc::new(RwLock::new(SequencerForkGraph {})); + // // create transaction processor, add accounts and programs, builtins, + // let processor = TransactionBatchProcessor::::default(); + // let mut cache = processor.program_cache.write().unwrap(); + + // // Initialize the mocked fork graph. + // // let fork_graph = Arc::new(RwLock::new(PayTubeForkGraph {})); + // cache.fork_graph = Some(Arc::downgrade(&fork_graph)); + + // let rent = Rent::default(); From 919efdea39f00a85f481daedf6b6451cd1a88523 Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Sun, 16 Feb 2025 22:40:41 +0100 Subject: [PATCH 12/41] completely connecting all to the database and testing it's work --- rollup_core/src/main.rs | 6 +- rollup_core/src/rollupdb.rs | 33 +++++++---- rollup_core/src/sequencer.rs | 110 ++++++++++++++++++----------------- 3 files changed, 83 insertions(+), 66 deletions(-) diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 50dbc83..2af4bf5 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -4,6 +4,7 @@ use actix_web::{web, App, HttpServer}; use async_channel; use frontend::FrontendMessage; use rollupdb::{RollupDB, RollupDBMessage}; +use solana_sdk::pubkey::Pubkey; use solana_sdk::{account::AccountSharedData, transaction::Transaction}; use tokio::runtime::Builder; use tokio::sync::oneshot; @@ -32,7 +33,8 @@ fn main() { // async // let (sequencer_sender, sequencer_receiver) = async_channel::bounded::(100); // Channel for communication between frontend and sequencer // let (rollupdb_sender, rollupdb_receiver) = async_channel::unbounded::(); // Channel for communication between sequencer and accountsdb let (frontend_sender, frontend_receiver) = async_channel::unbounded::(); // Channel for communication between data availability layer and frontend - let (account_sender, account_receiver) = async_channel::unbounded::>(); + pub type PubkeyAccountSharedData = Option>; + let (account_sender, account_receiver) = async_channel::unbounded::(); // std::thread::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); // let rt = Builder::new() @@ -51,7 +53,7 @@ fn main() { // async .unwrap(); - rt.spawn(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).unwrap()}); // .unwrap() + rt.spawn(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).await.unwrap()}); // .unwrap() // rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).unwrap()}); rt.block_on(RollupDB::run(rollupdb_receiver, fe_2, account_sender)); // rt.block_on(async { diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 176eea0..639ef2e 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -36,7 +36,7 @@ impl RollupDB { pub async fn run( rollup_db_receiver: CBReceiver, frontend_sender: Sender, - account_sender: Sender> + account_sender: Sender>> ) { let mut db = RollupDB { accounts_db: HashMap::new(), @@ -45,6 +45,7 @@ impl RollupDB { }; while let Ok(message) = rollup_db_receiver.recv() { if let Some(accounts_to_lock) = message.lock_accounts { + let mut information_to_send: Vec<(Pubkey, AccountSharedData)> = Vec::new(); log::info!("locking: {:?}", db.accounts_db); // Lock accounts, by removing them from the accounts_db hashmap, and adding them to locked accounts for pubkey in accounts_to_lock.iter() { @@ -61,11 +62,23 @@ impl RollupDB { .insert(pubkey.clone(), data); log::info!("account was not found"); } + + if let Some(account) = db.locked_accounts.get(&pubkey) { + // account_sender.send(Some(account.clone())).await.unwrap(); + information_to_send.push((*pubkey, account.clone())); + } + else { + // account_sender.send(None).await.unwrap(); + panic!() + } + } log::info!("locking done: {:?}", db.accounts_db); log::info!("locked accounts done: {:?}", db.locked_accounts); + log::info!("information to send -> {:?}", information_to_send); + account_sender.send(Some(information_to_send)).await.unwrap(); // log::info!("2: {:#?}", db.locked_accounts); } else if let Some(get_this_hash_tx) = message.frontend_get_tx { let req_tx = db.transactions.get(&get_this_hash_tx).unwrap(); @@ -102,16 +115,16 @@ impl RollupDB { // communication channel with database // communcation with the frontend } - else if let Some(pubkey) = message.get_account { + // else if let Some(pubkey) = message.get_account { - log::info!("4321: {:#?}", db.locked_accounts); - if let Some(account) = db.locked_accounts.get(&pubkey) { - account_sender.send(Some(account.clone())).await.unwrap(); - } - else { - account_sender.send(None).await.unwrap(); - } - } + // log::info!("4321: {:#?}", db.locked_accounts); + // if let Some(account) = db.locked_accounts.get(&pubkey) { + // account_sender.send(Some(account.clone())).await.unwrap(); + // } + // else { + // account_sender.send(None).await.unwrap(); + // } + // } } } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 6f62791..0137295 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -1,7 +1,6 @@ use core::panic; use std::{ - collections::{HashMap, HashSet}, - sync::{Arc, RwLock}, + collections::{HashMap, HashSet}, sync::{Arc, RwLock}, thread::sleep, time, vec }; use anyhow::{anyhow, Result}; @@ -28,10 +27,10 @@ use crate::loader::RollupAccountLoader; use crate::processor::*; use crate::errors::RollupErrors; -pub fn run( // async +pub async fn run( // async sequencer_receiver_channel: CBReceiver, // CBReceiver rollupdb_sender: CBSender, // CBSender - account_reciever: Receiver>, + account_reciever: Receiver>>, // rx: tokio::sync::oneshot::Receiver> // sync_ver_sender ) -> Result<()> { // let (tx, rx) = oneshot::channel(); // Create a channel to wait for response @@ -59,7 +58,18 @@ pub fn run( // async }) .map_err(|_| anyhow!("failed to send message to rollupdb"))?; - // rx.await.map_err(|_| anyhow!("failed to receive response"))?; + + if let Some(vec_of_accounts_data) = account_reciever.recv().await.unwrap() { + log::info!("received::: {:?}", vec_of_accounts_data); + for (pubkey, account) in vec_of_accounts_data.iter() { + rollup_account_loader.add_account(*pubkey, account.clone()); + log::info!("sucess:") + } + } + for pubkey in transaction.message.account_keys.iter(){ + let data = rollup_account_loader.get_account_shared_data(pubkey); + log::info!("data from an account: {:?}", data); + } let compute_budget = ComputeBudget::default(); let feature_set = FeatureSet::all_enabled(); @@ -70,42 +80,6 @@ pub fn run( // async let mut timings = ExecuteTimings::default(); let fork_graph = Arc::new(RwLock::new(RollupForkGraph {})); - let accounts_data = transaction // adding reference - .message - .account_keys - .iter() - .filter_map(|pubkey| { - rollupdb_sender.send(RollupDBMessage { - lock_accounts: None, - frontend_get_tx: None, - add_settle_proof: None, - add_new_data: None, - add_processed_transaction: None, - get_account: Some(*pubkey), - // response: Some(true) - }) - .map_err(|_| anyhow!("failed to send message to rollupdb")).unwrap(); - // rx.await.map_err(|_| anyhow!("failed to receive response"))?; - if let Ok(Some(account_data)) = account_reciever.try_recv() { - // If the pubkey exists in accounts_db, return the stored value - log::info!("account was recieved"); - log::info!("account was recieved, its data: {:?}", account_data); - Some((pubkey.clone(), account_data.clone())) - } else { - log::info!("account was copied from rpc"); - log::info!("account was copied from rpc, its data: {:?}", rpc_client_temp.get_account(pubkey).unwrap()); - // Otherwise, fetch from rpc_client_temp - // log::info!("Fetching account from rpc_client_temp"); - // log::info!("{:?}", pubkey); - match rpc_client_temp.get_account(pubkey) { - Ok(account) => Some((pubkey.clone(), account.into())), - Err(_) => None, // If the fetch fails, filter it out // SHOULD RETURN A CUSTOM ERROR - } - } - }) - .collect::>(); - log::info!("accounts_data: {:?}", &accounts_data); - let mut used_cu = 0u64; let sanitized = SanitizedTransaction::try_from_legacy_transaction( // to check here for the problem Transaction::from(transaction.clone()), @@ -114,19 +88,12 @@ pub fn run( // async log::info!("{:?}", sanitized.clone()); - let needed_programs: Vec<(Pubkey, AccountSharedData)> = - accounts_data - .iter() - .filter(|(pubkey, account)| account.executable()) - .map(|(pubkey, account)| (pubkey.clone(), account.clone())) - .collect(); - - - log::info!("mmmnnn: {:?}", accounts_data); - for (pubkey, account) in accounts_data.iter() { - rollup_account_loader.add_account(*pubkey, account.clone()); - } - log::info!("huylo: {:?}", rollup_account_loader.cache.read().unwrap()); + // let needed_programs: Vec<(Pubkey, AccountSharedData)> = + // accounts_data + // .iter() + // .filter(|(pubkey, account)| account.executable()) + // .map(|(pubkey, account)| (pubkey.clone(), account.clone())) + // .collect(); let processor = create_transaction_batch_processor( @@ -207,6 +174,41 @@ pub fn run( // async } Ok(()) } +// let accounts_data = transaction // adding reference + // .message + // .account_keys + // .iter() + // .filter_map(|pubkey| { + // rollupdb_sender.send(RollupDBMessage { + // lock_accounts: None, + // frontend_get_tx: None, + // add_settle_proof: None, + // add_new_data: None, + // add_processed_transaction: None, + // get_account: Some(*pubkey), + // // response: Some(true) + // }) + // .map_err(|_| anyhow!("failed to send message to rollupdb")).unwrap(); + // // rx.await.map_err(|_| anyhow!("failed to receive response"))?; + // if let Ok(Some(account_data)) = account_reciever.try_recv() { + // // If the pubkey exists in accounts_db, return the stored value + // log::info!("account was recieved"); + // log::info!("account was recieved, its data: {:?}", account_data); + // Some((pubkey.clone(), account_data.clone())) + // } else { + // log::info!("account was copied from rpc"); + // log::info!("account was copied from rpc, its data: {:?}", rpc_client_temp.get_account(pubkey).unwrap()); + // // Otherwise, fetch from rpc_client_temp + // // log::info!("Fetching account from rpc_client_temp"); + // // log::info!("{:?}", pubkey); + // match rpc_client_temp.get_account(pubkey) { + // Ok(account) => Some((pubkey.clone(), account.into())), + // Err(_) => None, // If the fetch fails, filter it out // SHOULD RETURN A CUSTOM ERROR + // } + // } + // }) + // .collect::>(); + // log::info!("accounts_data: {:?}", &accounts_data); // CREATE A PROOF FOR THE CHANGES STATE From 3a2784689c0d3fbc8618e18f4816d7e27de1af41 Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Mon, 17 Feb 2025 14:38:56 +0100 Subject: [PATCH 13/41] add fucntionality of checking locked accounts --- rollup_core/src/main.rs | 5 +++-- rollup_core/src/rollupdb.rs | 14 ++++++++++---- rollup_core/src/sequencer.rs | 24 ++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 2af4bf5..da9f4a8 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -35,6 +35,7 @@ fn main() { // async let (frontend_sender, frontend_receiver) = async_channel::unbounded::(); // Channel for communication between data availability layer and frontend pub type PubkeyAccountSharedData = Option>; let (account_sender, account_receiver) = async_channel::unbounded::(); + let (sender_locked_account, receiver_locked_account) = async_channel::unbounded::(); // std::thread::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); // let rt = Builder::new() @@ -53,9 +54,9 @@ fn main() { // async .unwrap(); - rt.spawn(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).await.unwrap()}); // .unwrap() + rt.spawn(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver, receiver_locked_account).await.unwrap()}); // .unwrap() // rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).unwrap()}); - rt.block_on(RollupDB::run(rollupdb_receiver, fe_2, account_sender)); + rt.block_on(RollupDB::run(rollupdb_receiver, fe_2, account_sender, sender_locked_account)); // rt.block_on(async { // tokio::spawn(async move { // RollupDB::run(rollupdb_receiver, fe_2, account_sender).await; diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 639ef2e..53ef9d1 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -36,7 +36,8 @@ impl RollupDB { pub async fn run( rollup_db_receiver: CBReceiver, frontend_sender: Sender, - account_sender: Sender>> + account_sender: Sender>>, + sender_locked_accounts: Sender, ) { let mut db = RollupDB { accounts_db: HashMap::new(), @@ -115,8 +116,13 @@ impl RollupDB { // communication channel with database // communcation with the frontend } - // else if let Some(pubkey) = message.get_account { - + else if let Some(pubkey) = message.get_account { + if db.locked_accounts.contains_key(&pubkey) { + sender_locked_accounts.send(true).await.unwrap(); + } else { + sender_locked_accounts.send(false).await.unwrap(); + } + // if // log::info!("4321: {:#?}", db.locked_accounts); // if let Some(account) = db.locked_accounts.get(&pubkey) { // account_sender.send(Some(account.clone())).await.unwrap(); @@ -124,7 +130,7 @@ impl RollupDB { // else { // account_sender.send(None).await.unwrap(); // } - // } + } } } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 0137295..7b4f55c 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -1,6 +1,6 @@ use core::panic; use std::{ - collections::{HashMap, HashSet}, sync::{Arc, RwLock}, thread::sleep, time, vec + collections::{HashMap, HashSet}, sync::{Arc, RwLock}, time, vec }; use anyhow::{anyhow, Result}; @@ -21,7 +21,7 @@ use solana_timings::ExecuteTimings; use solana_svm::{ message_processor::MessageProcessor, program_loader::load_program_with_pubkey, transaction_processing_callback::TransactionProcessingCallback, transaction_processing_result::ProcessedTransaction, transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment} }; -use tokio; +use tokio::time::{sleep, Duration}; use crate::{rollupdb::RollupDBMessage, settle::settle_state}; use crate::loader::RollupAccountLoader; use crate::processor::*; @@ -31,6 +31,7 @@ pub async fn run( // async sequencer_receiver_channel: CBReceiver, // CBReceiver rollupdb_sender: CBSender, // CBSender account_reciever: Receiver>>, + receiver_locked_accounts: Receiver, // rx: tokio::sync::oneshot::Receiver> // sync_ver_sender ) -> Result<()> { // let (tx, rx) = oneshot::channel(); // Create a channel to wait for response @@ -44,6 +45,25 @@ pub async fn run( // async ); while let transaction = sequencer_receiver_channel.recv().unwrap() { let accounts_to_lock = transaction.message.account_keys.clone(); + for pubkey in accounts_to_lock.iter() { + loop { + rollupdb_sender + .send(RollupDBMessage { + lock_accounts: None, + frontend_get_tx: None, + add_settle_proof: None, + add_new_data: None, + add_processed_transaction: None, + get_account: Some(*pubkey), + }) + + .map_err(|_| anyhow!("failed to send message to rollupdb"))?; + if receiver_locked_accounts.recv().await.unwrap() == false { + break; + } + sleep(Duration::from_millis(500)).await; + } + } tx_counter += 1; // lock accounts in rollupdb to keep paralell execution possible, just like on solana rollupdb_sender From fce84ed5e146a867a1cb7e0a65fe51deafd6e64b Mon Sep 17 00:00:00 2001 From: NkamaWilliams Date: Tue, 18 Feb 2025 12:20:18 +0100 Subject: [PATCH 14/41] better testing --- rollup_client/owner.json | 1 + rollup_client/src/main.rs | 24 ++++++++++++++++++++---- rollup_core/src/bundler.rs | 27 +++++++++++++++++++++++++-- rollup_core/src/rollupdb.rs | 9 +++++++-- rollup_core/src/sequencer.rs | 2 +- 5 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 rollup_client/owner.json diff --git a/rollup_client/owner.json b/rollup_client/owner.json new file mode 100644 index 0000000..a0cfc48 --- /dev/null +++ b/rollup_client/owner.json @@ -0,0 +1 @@ +[243,134,232,182,38,20,221,84,184,243,148,63,174,105,39,42,60,164,154,188,238,42,142,55,204,80,197,171,93,205,219,35,168,72,163,61,183,44,236,227,199,179,145,171,70,76,237,85,10,59,99,187,157,143,17,4,64,119,14,33,111,249,24,237] \ No newline at end of file diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index 2642374..e590748 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -31,6 +31,7 @@ pub struct GetTransaction { async fn main() -> Result<()> { let path = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/mykey_1.json"; let path2 = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/testkey.json"; + let path3 = "/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/owner.json"; let keypair = signer::keypair::read_keypair_file(path.to_string()).unwrap(); let keypair2 = signer::keypair::read_keypair_file(path2.to_string()).unwrap(); let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); @@ -91,13 +92,25 @@ async fn main() -> Result<()> { // println!("{tx_resp:#?}"); - let amounts: Vec = vec![4, -2, 3, -5, 1, -4, 2, -1, 3, -4]; + // let amounts: Vec = vec![4, -2, 3, -5, 1, -4, 2, -1, 3, -1]; + let amounts: Vec<(String, String, i32)> = vec![ + (path.to_string(), path2.to_string(), 5), + (path3.to_string(), path.to_string(), -3), + (path2.to_string(), path3.to_string(), 8), + (path.to_string(), path3.to_string(), -7), + (path2.to_string(), path.to_string(), 4), + (path3.to_string(), path2.to_string(), -6), + (path.to_string(), path2.to_string(), 9), + (path2.to_string(), path3.to_string(), -2), + (path3.to_string(), path.to_string(), 1), + (path.to_string(), path3.to_string(), -4), + ]; let mut txs: Vec = vec![]; for amt in amounts { - if amt > 0 { - txs.push(gen_transfer_tx(path.to_string(), path2.to_string(), amt as u64).await); + if amt.2 > 0 { + txs.push(gen_transfer_tx(amt.0, amt.1, amt.2 as u64).await); } else { - txs.push(gen_transfer_tx(path2.to_string(), path.to_string(), amt.abs() as u64).await); + txs.push(gen_transfer_tx(amt.1, amt.0, amt.2.abs() as u64).await); } } @@ -116,6 +129,9 @@ async fn main() -> Result<()> { println!("Submission {submission:#?}"); } + println!("KP: {}", keypair.pubkey()); + println!("KP2: {}", keypair2.pubkey()); + Ok(()) } diff --git a/rollup_core/src/bundler.rs b/rollup_core/src/bundler.rs index a00bdea..e46239a 100644 --- a/rollup_core/src/bundler.rs +++ b/rollup_core/src/bundler.rs @@ -38,7 +38,7 @@ impl TransferBundler { } } - pub fn parse_instruction(ix: &CompiledInstruction, account_keys: &[Pubkey]) -> Option<(Pubkey, Pubkey, i128)>{ + pub fn parse_compiled_instruction(ix: &CompiledInstruction, account_keys: &[Pubkey]) -> Option<(Pubkey, Pubkey, i128)>{ //Ensure the instruction is from System Program (where transfer is from) if ix.program_id_index as usize >= account_keys.len() || account_keys[ix.program_id_index as usize] != system_program::ID{ return None; @@ -60,13 +60,36 @@ impl TransferBundler { Some((from, to, amount as i128)) } + pub fn parse_instruction(ix: &Instruction) -> Option<(Pubkey, Pubkey, i128)>{ + //Enusre ix is owned by system program + if ix.program_id != system_program::ID{ + return None; + } + + //Ensure we have enough accounts + if ix.accounts.len() < 2{ + return None; + } + let from = ix.accounts[0].pubkey; + let to = ix.accounts[1].pubkey; + let amount = u64::from_le_bytes(ix.data[4..].try_into().ok()?); + + log::info!("FROM: {:?}", from.to_string()); + log::info!("TO: {:?}", to.to_string()); + log::info!("AMOUNT: {amount}"); + log::info!("IX DATA: {:?}", ix.data); + + + Some((from, to, amount as i128)) + } + //Parses transactions and add transfer ixs to TransferBundler pub fn bundle(&mut self, transaction: Transaction){ let ixs = get_transaction_instructions(&transaction); let account_keys: &[Pubkey] = &transaction.message.account_keys; for ix in ixs { if is_transfer_ix(&ix, account_keys){ - let (from, to, amount) = Self::parse_instruction(&ix, account_keys).unwrap(); + let (from, to, amount) = Self::parse_compiled_instruction(&ix, account_keys).unwrap(); let mut keys = [from, to]; keys.sort(); diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 10fe8eb..fb46535 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -90,8 +90,13 @@ impl RollupDB { for (_, tx) in db.transactions.clone() { tx_bundler.bundle(tx); } - let final_ix = tx_bundler.generate_final(); - log::info!("Final Transfer Ix: {final_ix:#?}"); + let final_ixs = tx_bundler.generate_final(); + log::info!("\nFinal Transfer Ixs:"); + for ix in final_ixs{ + if let Some((from, to, amount)) = TransferBundler::parse_instruction(&ix){ + } + } + log::info!("BUNDLING DONE"); db.transactions.clear(); } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 75dc531..a07b88a 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -172,7 +172,7 @@ pub fn run( //View sent processed tx details let ixs = get_transaction_instructions(&transaction); let acc_keys: &[Pubkey] = &transaction.message.account_keys; - if let Some((from, to, amount)) = TransferBundler::parse_instruction(&ixs[0], acc_keys) { + if let Some((from, to, amount)) = TransferBundler::parse_compiled_instruction(&ixs[0], acc_keys) { log::info!(" Transaction Info\n From: {from:?}\n From 54780f218b68d2b5f16e6eb61920ce0806d3b7d3 Mon Sep 17 00:00:00 2001 From: timadigwe Date: Tue, 18 Feb 2025 14:16:34 +0100 Subject: [PATCH 15/41] delegation service in the frontend --- rollup_core/Cargo.toml | 3 +- rollup_core/src/delegation.rs | 50 ++++ rollup_core/src/delegation_service.rs | 85 +++++++ rollup_core/src/frontend.rs | 56 ++++- rollup_core/src/main.rs | 24 +- rollup_core/src/processor.rs | 8 +- rollup_core/src/rollupdb.rs | 10 + rollup_core/src/sequencer.rs | 333 +------------------------- 8 files changed, 222 insertions(+), 347 deletions(-) create mode 100644 rollup_core/src/delegation.rs create mode 100644 rollup_core/src/delegation_service.rs diff --git a/rollup_core/Cargo.toml b/rollup_core/Cargo.toml index da4aaa2..17e4c4f 100644 --- a/rollup_core/Cargo.toml +++ b/rollup_core/Cargo.toml @@ -21,4 +21,5 @@ env_logger = "0.11.5" log = "0.4.22" anyhow = "1.0.86" crossbeam = "0.8.4" -async-channel = "2.3.1" \ No newline at end of file +async-channel = "2.3.1" +borsh = "0.10" \ No newline at end of file diff --git a/rollup_core/src/delegation.rs b/rollup_core/src/delegation.rs new file mode 100644 index 0000000..333164c --- /dev/null +++ b/rollup_core/src/delegation.rs @@ -0,0 +1,50 @@ +use solana_sdk::{ + pubkey::Pubkey, + instruction::{AccountMeta, Instruction}, + system_program, +}; +use borsh::{BorshSerialize, BorshDeserialize}; + +#[derive(BorshSerialize, BorshDeserialize)] +pub enum DelegationInstruction { + InitializeDelegate { amount: u64 }, + Withdraw { amount: u64 }, +} + +#[derive(BorshSerialize, BorshDeserialize)] +pub struct DelegatedAccount { + pub owner: Pubkey, + pub delegated_amount: u64, + pub last_deposit_time: i64, + pub bump: u8, +} + +pub fn get_delegation_program_id() -> Pubkey { + "E1bxy4HwKFjPARhVe7NjvoFtynN69C4xNA53uSwruHrP" + .parse() + .unwrap() +} + +pub fn find_delegation_pda(owner: &Pubkey) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[b"delegate", owner.as_ref()], + &get_delegation_program_id() + ) +} + +pub fn create_delegation_instruction( + owner: &Pubkey, + amount: u64, +) -> Instruction { + let (pda, _) = find_delegation_pda(owner); + + Instruction { + program_id: get_delegation_program_id(), + accounts: vec![ + AccountMeta::new(*owner, true), + AccountMeta::new(pda, false), + AccountMeta::new_readonly(system_program::id(), false), + ], + data: borsh::to_vec(&DelegationInstruction::InitializeDelegate { amount }).unwrap(), + } +} \ No newline at end of file diff --git a/rollup_core/src/delegation_service.rs b/rollup_core/src/delegation_service.rs new file mode 100644 index 0000000..d4213f8 --- /dev/null +++ b/rollup_core/src/delegation_service.rs @@ -0,0 +1,85 @@ +use { + crate::delegation::{find_delegation_pda, DelegatedAccount, create_delegation_instruction}, + solana_sdk::{ + account::{AccountSharedData, ReadableAccount}, + pubkey::Pubkey, + transaction::Transaction, + message::Message, + }, + solana_client::rpc_client::RpcClient, + anyhow::{Result, anyhow}, + std::collections::HashMap, + borsh::BorshDeserialize, +}; + +pub struct DelegationService { + rpc_client: RpcClient, + pda_cache: HashMap, +} + +impl DelegationService { + pub fn new(rpc_url: &str) -> Self { + Self { + rpc_client: RpcClient::new(rpc_url.to_string()), + pda_cache: HashMap::new(), + } + } + + pub fn get_or_fetch_pda(&mut self, user: &Pubkey) -> Result> { + let (pda, _) = find_delegation_pda(user); + + // Try cache first + if let Some(account) = self.pda_cache.get(&pda) { + if let Ok(delegation) = DelegatedAccount::try_from_slice(&account.data()) { + return Ok(Some((pda, delegation))); + } + } + + // If not in cache, try fetching from chain + match self.rpc_client.get_account(&pda) { + Ok(account) => { + if let Ok(delegation) = DelegatedAccount::try_from_slice(&account.data()) { + self.pda_cache.insert(pda, account.into()); + Ok(Some((pda, delegation))) + } else { + Ok(None) + } + } + Err(_) => Ok(None) + } + } + + pub fn verify_delegation_for_transaction( + &mut self, + user: &Pubkey, + required_amount: u64, + ) -> Result> { // Returns PDA if delegation exists and is sufficient + if let Some((pda, delegation)) = self.get_or_fetch_pda(user)? { + if delegation.delegated_amount >= required_amount { + Ok(Some(pda)) + } else { + Ok(None) + } + } else { + Ok(None) + } + } + + pub fn create_delegation_transaction( + &self, + user: &Pubkey, + amount: u64, + ) -> Result { + let instruction = create_delegation_instruction(user, amount); + let recent_blockhash = self.rpc_client.get_latest_blockhash()?; + + Ok(Transaction::new_with_payer( + &[instruction], + Some(user), + )) + } + + pub fn update_pda_state(&mut self, pda: Pubkey, account: AccountSharedData) { + self.pda_cache.insert(pda, account); + } +} diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index 1c7f7fc..4d38f51 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -9,6 +9,11 @@ use crossbeam::channel::{Sender as CBSender, Receiver as CBReceiver}; use serde::{Deserialize, Serialize}; use solana_sdk::hash::Hash; // keccak::Hash use solana_sdk::transaction::Transaction; +use { + crate::delegation_service::DelegationService, + solana_sdk::pubkey::Pubkey, + std::sync::{RwLock}, +}; use crate::rollupdb::RollupDBMessage; @@ -32,23 +37,52 @@ pub struct RollupTransaction { sol_transaction: Transaction, } +#[derive(Serialize)] +pub enum TransactionResponse { + Success { message: String }, + NeedsDelegation { delegation_tx: Transaction }, + Error { message: String }, +} + pub async fn submit_transaction( body: web::Json, sequencer_sender: web::Data>, - // rollupdb_sender: web::Data>, + delegation_service: web::Data>>, ) -> actix_web::Result { - // Validate transaction structure with serialization in function signature - log::info!("Submitted transaction"); - log::info!("{body:?}"); + let tx = &body.sol_transaction; + let payer = tx.message.account_keys[0]; + let amount = 1_000_000; // Extract actual amount - // Send transaction to sequencer - sequencer_sender - .send(body.sol_transaction.clone()) - - .unwrap(); + // Check delegation and get PDA + let delegation_result = delegation_service.write().unwrap() + .verify_delegation_for_transaction(&payer, amount) + .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; + + match delegation_result { + Some(pda) => { + // Modify transaction to use PDA + let mut modified_tx = tx.clone(); + modified_tx.message.account_keys[0] = pda; + + // Send modified transaction to sequencer + sequencer_sender.send(modified_tx) + .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; - // Return response - Ok(HttpResponse::Ok().json(HashMap::from([("Transaction status", "Submitted")]))) + Ok(HttpResponse::Ok().json(TransactionResponse::Success { + message: "Transaction submitted".to_string() + })) + } + None => { + // Create delegation transaction + let delegation_tx = delegation_service.write().unwrap() + .create_delegation_transaction(&payer, amount) + .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; + + Ok(HttpResponse::Ok().json(TransactionResponse::NeedsDelegation { + delegation_tx + })) + } + } } pub async fn get_transaction( diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index da9f4a8..d800f42 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -1,4 +1,6 @@ use std::thread; +use std::sync::{Arc, RwLock}; +use crate::delegation_service::DelegationService; use actix_web::{web, App, HttpServer}; use async_channel; @@ -16,6 +18,8 @@ mod settle; mod processor; mod loader; mod errors; +mod delegation; +mod delegation_service; // #[actix_web::main] // #[tokio::main] @@ -46,6 +50,9 @@ fn main() { // async let db_sender2 = rollupdb_sender.clone(); let fe_2 = frontend_sender.clone(); + let delegation_service = Arc::new(RwLock::new( + DelegationService::new("https://api.devnet.solana.com") + )); let asdserver_thread = thread::spawn(|| { let rt = Builder::new_multi_thread() @@ -53,15 +60,15 @@ fn main() { // async .build() .unwrap(); - - rt.spawn(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver, receiver_locked_account).await.unwrap()}); // .unwrap() - // rt.block_on(async {sequencer::run(sequencer_receiver, db_sender2, account_receiver).unwrap()}); + rt.spawn(async { + sequencer::run( + sequencer_receiver, + db_sender2, + account_receiver, + receiver_locked_account, + ).await.unwrap() + }); rt.block_on(RollupDB::run(rollupdb_receiver, fe_2, account_sender, sender_locked_account)); - // rt.block_on(async { - // tokio::spawn(async move { - // RollupDB::run(rollupdb_receiver, fe_2, account_sender).await; - // }); - // }); }); // Create sequencer task // tokio::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); @@ -89,6 +96,7 @@ fn main() { // async .app_data(web::Data::new(rollupdb_sender.clone())) .app_data(web::Data::new(frontend_sender.clone())) .app_data(web::Data::new(frontend_receiver.clone())) + .app_data(web::Data::new(delegation_service.clone())) .route("/", web::get().to(frontend::test)) .route( "/get_transaction", diff --git a/rollup_core/src/processor.rs b/rollup_core/src/processor.rs index 90e2264..37238f9 100644 --- a/rollup_core/src/processor.rs +++ b/rollup_core/src/processor.rs @@ -103,10 +103,10 @@ pub(crate) fn get_transaction_check_results( lamports_per_signature: u64, ) -> Vec> { vec![ - transaction::Result::Ok(CheckedTransactionDetails { - nonce: None, - lamports_per_signature, - }); + transaction::Result::Ok(CheckedTransactionDetails::new( + None, + lamports_per_signature + )); len ] } diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 53ef9d1..efca84f 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -29,6 +29,7 @@ pub struct RollupDB { accounts_db: HashMap, locked_accounts: HashMap, transactions: HashMap, + pda_mappings: HashMap, // user -> pda mapping // async_ver_recv: Receiver> } @@ -43,6 +44,7 @@ impl RollupDB { accounts_db: HashMap::new(), locked_accounts: HashMap::new(), transactions: HashMap::new(), + pda_mappings: HashMap::new(), }; while let Ok(message) = rollup_db_receiver.recv() { if let Some(accounts_to_lock) = message.lock_accounts { @@ -133,6 +135,14 @@ impl RollupDB { } } } + + pub fn register_pda(&mut self, user: Pubkey, pda: Pubkey) { + self.pda_mappings.insert(user, pda); + } + + pub fn get_pda_for_user(&self, user: &Pubkey) -> Option<&Pubkey> { + self.pda_mappings.get(user) + } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 7b4f55c..76a05d2 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -19,23 +19,21 @@ use solana_sdk::{ }; use solana_timings::ExecuteTimings; use solana_svm::{ - message_processor::MessageProcessor, program_loader::load_program_with_pubkey, transaction_processing_callback::TransactionProcessingCallback, transaction_processing_result::ProcessedTransaction, transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment} +transaction_processing_callback::TransactionProcessingCallback, transaction_processing_result::ProcessedTransaction, transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment} }; use tokio::time::{sleep, Duration}; use crate::{rollupdb::RollupDBMessage, settle::settle_state}; use crate::loader::RollupAccountLoader; use crate::processor::*; use crate::errors::RollupErrors; +use crate::delegation_service::DelegationService; -pub async fn run( // async - sequencer_receiver_channel: CBReceiver, // CBReceiver - rollupdb_sender: CBSender, // CBSender +pub async fn run( + sequencer_receiver_channel: CBReceiver, + rollupdb_sender: CBSender, account_reciever: Receiver>>, receiver_locked_accounts: Receiver, - // rx: tokio::sync::oneshot::Receiver> // sync_ver_sender ) -> Result<()> { - // let (tx, rx) = oneshot::channel(); // Create a channel to wait for response - let mut tx_counter = 0u32; let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); @@ -108,13 +106,8 @@ pub async fn run( // async log::info!("{:?}", sanitized.clone()); - // let needed_programs: Vec<(Pubkey, AccountSharedData)> = - // accounts_data - // .iter() - // .filter(|(pubkey, account)| account.executable()) - // .map(|(pubkey, account)| (pubkey.clone(), account.clone())) - // .collect(); - + let payer = transaction.message.account_keys[0]; + let amount = 1_000_000; // For now, using a fixed amount. Replace with actual amount extraction let processor = create_transaction_batch_processor( &rollup_account_loader, @@ -128,12 +121,11 @@ pub async fn run( // async let processing_environment = TransactionProcessingEnvironment { blockhash: Hash::default(), - epoch_total_stake: None, - epoch_vote_accounts: None, + epoch_total_stake: 0u64, feature_set: Arc::new(feature_set), - fee_structure: Some(&fee_structure), - lamports_per_signature: fee_structure.lamports_per_signature, rent_collector: Some(&rent_collector), + blockhash_lamports_per_signature: fee_structure.lamports_per_signature, + fee_lamports_per_signature: fee_structure.lamports_per_signature, }; let processing_config = TransactionProcessingConfig { @@ -194,308 +186,3 @@ pub async fn run( // async } Ok(()) } -// let accounts_data = transaction // adding reference - // .message - // .account_keys - // .iter() - // .filter_map(|pubkey| { - // rollupdb_sender.send(RollupDBMessage { - // lock_accounts: None, - // frontend_get_tx: None, - // add_settle_proof: None, - // add_new_data: None, - // add_processed_transaction: None, - // get_account: Some(*pubkey), - // // response: Some(true) - // }) - // .map_err(|_| anyhow!("failed to send message to rollupdb")).unwrap(); - // // rx.await.map_err(|_| anyhow!("failed to receive response"))?; - // if let Ok(Some(account_data)) = account_reciever.try_recv() { - // // If the pubkey exists in accounts_db, return the stored value - // log::info!("account was recieved"); - // log::info!("account was recieved, its data: {:?}", account_data); - // Some((pubkey.clone(), account_data.clone())) - // } else { - // log::info!("account was copied from rpc"); - // log::info!("account was copied from rpc, its data: {:?}", rpc_client_temp.get_account(pubkey).unwrap()); - // // Otherwise, fetch from rpc_client_temp - // // log::info!("Fetching account from rpc_client_temp"); - // // log::info!("{:?}", pubkey); - // match rpc_client_temp.get_account(pubkey) { - // Ok(account) => Some((pubkey.clone(), account.into())), - // Err(_) => None, // If the fetch fails, filter it out // SHOULD RETURN A CUSTOM ERROR - // } - // } - // }) - // .collect::>(); - // log::info!("accounts_data: {:?}", &accounts_data); - - - // CREATE A PROOF FOR THE CHANGES STATE - -// Lock db to avoid state changes during settlement - - // Prepare root hash, or your own proof to send to chain - - // Send proof to chain - - // let _settle_tx_hash = settle_state("proof".into()).await?; - // .map(|pubkey| { - // ( - // pubkey.clone(), - // rpc_client_temp.get_account(pubkey).unwrap().into(), - // ) - // }) - - -// log::info!("accounts_data: {needed_programs:?}"); - - // for (pubkey, account) in needed_programs.iter() { - // rollup_account_loader.add_account(*pubkey, account.clone()); - // } - -// let rent_collector = RentCollector::default(); - - // Solana runtime. - // let fork_graph = Arc::new(RwLock::new(SequencerForkGraph {})); - - // // create transaction processor, add accounts and programs, builtins, - // let processor = TransactionBatchProcessor::::default(); - - // let mut cache = processor.program_cache.write().unwrap(); - - // // Initialize the mocked fork graph. - // // let fork_graph = Arc::new(RwLock::new(PayTubeForkGraph {})); - // cache.fork_graph = Some(Arc::downgrade(&fork_graph)); - - // let rent = Rent::default(); - - - - // //****************************************************************************************************/ - // // let instructions = &transaction.message.instructions; - // // // let index_array_of_program_pubkeys = Vec::with_capacity(instructions.len()); - // // let program_ids = &transaction.message.account_keys; - - // // let needed_programs: Vec<&Pubkey> = instructions - // // .iter() - // // .map( - // // |instruction| - // // instruction.program_id(program_ids)).collect(); - // //****************************************************************************************************/ - - // let mut transaction_context = TransactionContext::new( - // accounts_data, - // Rent::default(), - // compute_budget.max_instruction_stack_depth, - // compute_budget.max_instruction_trace_length, - // ); - // // transaction_context.get_current_instruction_context().unwrap().get_index_of_program_account_in_transaction(2).unwrap(); - // // transaction_context.push(); - - - // // here we have to load them somehow - - // let runtime_env = Arc::new( - // create_program_runtime_environment_v1(&feature_set, &compute_budget, false, false) - // .unwrap(), - // ); - - // let mut prog_cache = ProgramCacheForTxBatch::new( - // Slot::default(), - // ProgramRuntimeEnvironments { - // program_runtime_v1: runtime_env.clone(), - // program_runtime_v2: runtime_env, - // }, - // None, - // Epoch::default(), - // ); - - - // // prog_cache.replenish(accounts_data., entry) - - // let sysvar_c = sysvar_cache::SysvarCache::default(); - // let env = EnvironmentConfig::new( - // Hash::default(), - // None, - // None, - // Arc::new(feature_set), - // lamports_per_signature, - // &sysvar_c, - // ); - // // let default_env = EnvironmentConfig::new(blockhash, epoch_total_stake, epoch_vote_accounts, feature_set, lamports_per_signature, sysvar_cache) - - // // let processing_environment = TransactionProcessingEnvironment { - // // blockhash: Hash::default(), - // // epoch_total_stake: None, - // // epoch_vote_accounts: None, - // // feature_set: Arc::new(feature_set), - // // fee_structure: Some(&fee_structure), - // // lamports_per_signature, - // // rent_collector: Some(&rent_collector), - // // }; - - - - // // for (pubkey, account) in rollup_account_loader.cache.read().unwrap().iter() { - // // let _p = rollup_account_loader.get_account_shared_data(pubkey); - // // log::info!("account: {_p:?}"); - // // } - // // let cache = &rollup_account_loader.cache.read().unwrap(); - // // let pew = cache.keys().next().cloned().unwrap(); - // // let owner = cache.get(&pew).unwrap().owner(); - // // log::debug!("pubkey: {owner:?}"); - - - // let program_cache_entry = load_program_with_pubkey( - // &rollup_account_loader, - // &prog_cache.environments, - // &rollup_account_loader.cache.read().unwrap().keys().next().cloned().unwrap(),//&needed_programs[0].0, - // 0, - // &mut ExecuteTimings::default(), - // false - // ); - - // log::info!("program_cache_entry: {program_cache_entry:?}"); - - // prog_cache.replenish( - // needed_programs[0].0, - // program_cache_entry.unwrap(), - // ); - // // { - // // let instruction_ctx = transaction_context.get_current_instruction_context(); - // // log::debug!("instruction_ctx: {instruction_ctx:?}"); - // // } - // // let instruction_ctx_height = transaction_context.get_instruction_context_stack_height(); - - // // log::debug!("instruction_ctx_height: {instruction_ctx_height}"); - - // // let instruction_ctx_next = transaction_context.get_next_instruction_context(); - // // // let instruction_ctx = transaction_context.get_next_instruction_context(); - - // // log::debug!("instruction_ctx: {instruction_ctx_next:?}"); - - - - // let mut invoke_context = InvokeContext::new( - // &mut transaction_context, - // &mut prog_cache, - // env, - // None, - // compute_budget.to_owned() - // ); - - - // // let instruction_ctx_2 = invoke_context.transaction_context.get_current_instruction_context(); - // // log::debug!("instruction_ctx_2: {instruction_ctx_2:?}"); - // // let instruction_ctx_height = invoke_context.transaction_context.get_instruction_context_stack_height(); - // // log::debug!("instruction_ctx_height: {instruction_ctx_height}"); - // // let instruction_ctx_height = invoke_context.transaction_context.get_instruction_context_at_index_in_trace(0); - // // log::debug!("instruction_ctx_height: {instruction_ctx_height:?}"); - - - - - // // HAS TO BE AN ADDRESS OF THE PROGRAM - - // // invoke_context.program_cache_for_tx_batch.replenish(key, program_cache_entry.unwrap()); - - - - - - - - // // let account_index = invoke_context - // // .transaction_context - // // .find_index_of_account(&instructions::id()); - - // // if account_index.is_none() { - // // panic!("Could not find instructions account"); - // // } - - // let program_indices: Vec = vec![0]; - // let result_msg = MessageProcessor::process_message( - // &sanitized.unwrap().message().to_owned(), // ERROR WITH SOLANA_SVM VERSION - // // ?should be fixed with help of chagning versions of solana-svm ? - // // &sanitized.unwrap().message().to_owned(), - // &[program_indices], // TODO: automotize this process - // &mut invoke_context, - // &mut timings, - // &mut used_cu, - // ); - - // log::info!("{:?}", &result_msg); - // log::info!("The message was done sucessfully"); - - - - - - -// TWO WAYS -> TRANSACTIONBATCHPROCCESOR OR MESSAGEPROCESSOR - -// PAYTUBE in SVM FOLDER - -// The question of how often to pull/push the state out of mainnet state - -// PDA as a *treasury , to solve problem with sol that could disapear from account - -// to create kind of a program that will lock funds on mainnet - -// MagicBlock relyaing on their infrustructure - -// To make a buffer between sending two transactions - - - - -// / In order to use the `TransactionBatchProcessor`, another trait - Solana -// / Program Runtime's `ForkGraph` - must be implemented, to tell the batch -// / processor how to work across forks. -// / -// /// Since our rollup doesn't use slots or forks, this implementation is mocked. -// pub(crate) struct SequencerForkGraph {} - -// impl ForkGraph for SequencerForkGraph { -// fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation { -// BlockRelation::Unknown -// } -// } -// pub struct SequencerAccountLoader<'a> { -// cache: RwLock>, -// rpc_client: &'a RpcClient, -// } - -// impl<'a> SequencerAccountLoader<'a> { -// pub fn new(rpc_client: &'a RpcClient) -> Self { -// Self { -// cache: RwLock::new(HashMap::new()), -// rpc_client, -// } -// } -// } - -// / Implementation of the SVM API's `TransactionProcessingCallback` interface. -// / -// / The SVM API requires this plugin be provided to provide the SVM with the -// / ability to load accounts. -// / -// / In the Agave validator, this implementation is Bank, powered by AccountsDB. -// impl TransactionProcessingCallback for SequencerAccountLoader<'_> { -// fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option { -// if let Some(account) = self.cache.read().unwrap().get(pubkey) { -// return Some(account.clone()); -// } - -// let account: AccountSharedData = self.rpc_client.get_account(pubkey).ok()?.into(); -// self.cache.write().unwrap().insert(*pubkey, account.clone()); - -// Some(account) -// } - -// fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option { -// self.get_account_shared_data(account) -// .and_then(|account| owners.iter().position(|key| account.owner().eq(key))) -// } -// } From 918a5a4e09bdc14abdb509689f0e24285fc76b75 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 21:58:55 +0700 Subject: [PATCH 16/41] Create README.md --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc95d9b --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Rollup for our SVM class From 2f8182fbe23dc05afb28c1fc4fa8537f85f4bc74 Mon Sep 17 00:00:00 2001 From: NkamaWilliams Date: Tue, 18 Feb 2025 16:08:39 +0100 Subject: [PATCH 17/41] updated keypair path --- rollup_client/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index 467b400..de73894 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -28,8 +28,8 @@ pub struct GetTransaction { #[tokio::main] async fn main() -> Result<()> { - let keypair = signer::keypair::read_keypair_file("/Users/nicknut/Desktop/Q1_SVM/Rollup_SVM_Q1/Basic_Rollup_fork/rollup_client/mykey_1.json").unwrap(); - let keypair2 = signer::keypair::read_keypair_file("/Users/nicknut/Desktop/Q1_SVM/Rollup_SVM_Q1/Basic_Rollup_fork/rollup_client/testkey.json").unwrap(); + let keypair = signer::keypair::read_keypair_file("/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/mykey_1.json").unwrap(); + let keypair2 = signer::keypair::read_keypair_file("/home/izomana/adv-svm/Basic_Rollup_fork/rollup_client/testkey.json").unwrap(); let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); let ix = From 64c273d9e320d2e503f68b9d91b2dfdc5d9cfa25 Mon Sep 17 00:00:00 2001 From: NkamaWilliams Date: Tue, 18 Feb 2025 16:49:24 +0100 Subject: [PATCH 18/41] merged testing | williams --- rollup_core/src/rollupdb.rs | 3 +++ rollup_core/src/sequencer.rs | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index ea335b8..b9ffa5f 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -21,6 +21,7 @@ pub struct RollupDBMessage { pub frontend_get_tx: Option, pub add_settle_proof: Option, pub get_account: Option, + pub add_new_data: Option>, // pub response: Option, //Testing purposes pub bundle_tx: bool @@ -37,6 +38,8 @@ impl RollupDB { pub async fn run( rollup_db_receiver: CBReceiver, frontend_sender: Sender, + account_sender: Sender>>, + sender_locked_accounts: Sender, ) { let mut db = RollupDB { accounts_db: HashMap::new(), diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 8a162e6..0d76347 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -56,6 +56,7 @@ pub async fn run( // async add_new_data: None, add_processed_transaction: None, get_account: Some(*pubkey), + bundle_tx: false }) .map_err(|_| anyhow!("failed to send message to rollupdb"))?; @@ -210,9 +211,15 @@ pub async fn run( // async lock_accounts: None, add_processed_transaction: None, add_settle_proof: None, + get_account: None, + add_new_data: None, frontend_get_tx: None, bundle_tx: true }).unwrap(); + } + } + Ok(()) +} // Lock db to avoid state changes during settlement From 5b415a1eb70fde715225bb74a38f1b530ba52153 Mon Sep 17 00:00:00 2001 From: NkamaWilliams Date: Tue, 18 Feb 2025 17:17:23 +0100 Subject: [PATCH 19/41] fixed bugs in rollup --- rollup_core/src/rollupdb.rs | 63 +++++++++++++++++++++++++++--------- rollup_core/src/sequencer.rs | 1 + 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index b9ffa5f..954fb9d 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -10,6 +10,8 @@ use std::{ collections::{HashMap, HashSet}, default, }; +use solana_client::rpc_client::{RpcClient}; + use crate::frontend::FrontendMessage; use crate::bundler::*; @@ -50,11 +52,41 @@ impl RollupDB { while let Ok(message) = rollup_db_receiver.recv() { log::info!("Received RollupDBMessage"); if let Some(accounts_to_lock) = message.lock_accounts { + let mut information_to_send: Vec<(Pubkey, AccountSharedData)> = Vec::new(); + log::info!("locking: {:?}", db.accounts_db); // Lock accounts, by removing them from the accounts_db hashmap, and adding them to locked accounts - let _ = accounts_to_lock.iter().map(|pubkey| { - db.locked_accounts - .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()) - }); + for pubkey in accounts_to_lock.iter() { + if let Some(account) = db.accounts_db.get(pubkey) { + db.locked_accounts + .insert(pubkey.clone(), db.accounts_db.remove(pubkey).unwrap()); + log::info!("account was found"); + } + else { + let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); + let account = rpc_client_temp.get_account(pubkey).unwrap(); + let data: AccountSharedData = account.into(); + db.locked_accounts + .insert(pubkey.clone(), data); + log::info!("account was not found"); + } + + if let Some(account) = db.locked_accounts.get(&pubkey) { + // account_sender.send(Some(account.clone())).await.unwrap(); + information_to_send.push((*pubkey, account.clone())); + } + else { + // account_sender.send(None).await.unwrap(); + panic!() + } + + } + log::info!("locking done: {:?}", db.accounts_db); + log::info!("locked accounts done: {:?}", db.locked_accounts); + + + log::info!("information to send -> {:?}", information_to_send); + account_sender.send(Some(information_to_send)).await.unwrap(); + // log::info!("2: {:#?}", db.locked_accounts); } else if let Some(get_this_hash_tx) = message.frontend_get_tx { log::info!("Getting tx for frontend"); let req_tx = db.transactions.get(&get_this_hash_tx).unwrap(); @@ -69,26 +101,24 @@ impl RollupDB { } else if let Some(tx) = message.add_processed_transaction { let processed_data = message.add_new_data.unwrap(); - log::info!("Adding processed tx"); + // unlocking accounts let locked_keys = tx.message.account_keys.clone(); // get the keys + log::info!("it is starting accounts_db{:#?}", db.accounts_db); + log::info!("it is starting locked_db{:#?}", db.locked_accounts); + for (pubkey, data) in processed_data.iter() { + db.locked_accounts.remove(pubkey).unwrap(); + db.accounts_db.insert(*pubkey, data.clone()); + log::info!("it is final accounts_db{:#?}", db.accounts_db); + log::info!("it is final locked_db{:#?}", db.locked_accounts); + - // locked_keys.iter().for_each( - // |pubkey| if db.locked_accounts.contains_key(&pubkey) { - // db.locked_accounts.remove(&pubkey); - // } - // ); - - for pubkey in locked_keys { - if let Some(account) = db.locked_accounts.remove(&pubkey) { - db.accounts_db.insert(pubkey, account); // Unlock and restore - } } // send transaction to the db.transactions db.transactions.insert(tx.message.hash(), tx.clone()); log::info!("locked: {:#?}", db.locked_accounts); - log::info!("43210: {:#?}", db.accounts_db); log::info!("PROCESSED TX: {}", db.transactions.len()); + log::info!("43210: {:#?}", db.accounts_db); // communication channel with database // communcation with the frontend @@ -122,6 +152,7 @@ impl RollupDB { // account_sender.send(None).await.unwrap(); // } } + } } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 0d76347..2f82c23 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -206,6 +206,7 @@ pub async fn run( // async // Call settle if transaction amount since last settle hits 10 if tx_counter >= 10 { + log::info!("Start bundling!"); //bundle transfer tx test rollupdb_sender.send(RollupDBMessage { lock_accounts: None, From a75dfe31599a0e997cc274e4df36a56aa51eae38 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:22:00 +0700 Subject: [PATCH 20/41] Update README.md --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc95d9b..0e4d9de 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ -Rollup for our SVM class +## Overview +A rollup is a Layer-2 scaling solution that processes and bundles transactions off-chain before settling them on the main chain. This reduces congestion and fees while keeping the security of the underlying blockchain. + +## Why Rollups for Solana? +Rollups can greatly enhance Solana by: +- **Increasing Throughput:** Offload transactions from the main chain. +- **Reducing Fees:** Batch transactions to lower costs. +- **Offering Flexibility:** Allow for customized transaction processing without changing Solana’s core. + +## Features +- **Fetches Transactions:** Efficiently retrieves incoming transactions. +- **Delegates Funds:** Uses a dedicated Solana program to manage fund delegation. +- **Sends to the Sequencer:** For ordered and structured processing. +- **Locks, Loads, and Executes Transactions:** Ensures data integrity during processing. +- **Updates Local State:** Keeps the current state updated with processed transactions. +- **Bundles Similar Transactions:** Groups similar transactions into one. +- **Batch Bundling:** After 10 transactions, bundles them into a single unit. +- **Settles Changes to the Chain:** Commits batched changes back to Solana. From ee5e84c1a3b0a0b54d7882673113ca92ea33bf25 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:24:30 +0700 Subject: [PATCH 21/41] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0e4d9de..07b573b 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ ## Overview A rollup is a Layer-2 scaling solution that processes and bundles transactions off-chain before settling them on the main chain. This reduces congestion and fees while keeping the security of the underlying blockchain. -## Why Rollups for Solana? -Rollups can greatly enhance Solana by: +## Why would Solana need a rollup? +Rollups can enhance Solana by: - **Increasing Throughput:** Offload transactions from the main chain. -- **Reducing Fees:** Batch transactions to lower costs. -- **Offering Flexibility:** Allow for customized transaction processing without changing Solana’s core. +- **Lower fees:** Batch transactions to lower costs. +- **Flexibility:** Allow for customized transaction processing without changing Solana’s core. ## Features -- **Fetches Transactions:** Efficiently retrieves incoming transactions. -- **Delegates Funds:** Uses a dedicated Solana program to manage fund delegation. -- **Sends to the Sequencer:** For ordered and structured processing. -- **Locks, Loads, and Executes Transactions:** Ensures data integrity during processing. -- **Updates Local State:** Keeps the current state updated with processed transactions. +- **Fetches Transactions:** +- **Delegates Funds:** Solana program +- **Sends to the Sequencer:** +- **Locks accounts, Loads, and executes Transactions:** +- **Updates Local State:** - **Bundles Similar Transactions:** Groups similar transactions into one. - **Batch Bundling:** After 10 transactions, bundles them into a single unit. - **Settles Changes to the Chain:** Commits batched changes back to Solana. From 958c2ea41d714bddbd40435ed043f898b7d0a918 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:26:39 +0700 Subject: [PATCH 22/41] Update README.md --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 07b573b..991c802 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +## SVM rollup +As part of the Turbin3 SVM cohort, our team built our own SVM rollup. + ## Overview A rollup is a Layer-2 scaling solution that processes and bundles transactions off-chain before settling them on the main chain. This reduces congestion and fees while keeping the security of the underlying blockchain. @@ -7,12 +10,12 @@ Rollups can enhance Solana by: - **Lower fees:** Batch transactions to lower costs. - **Flexibility:** Allow for customized transaction processing without changing Solana’s core. -## Features -- **Fetches Transactions:** -- **Delegates Funds:** Solana program -- **Sends to the Sequencer:** -- **Locks accounts, Loads, and executes Transactions:** -- **Updates Local State:** -- **Bundles Similar Transactions:** Groups similar transactions into one. -- **Batch Bundling:** After 10 transactions, bundles them into a single unit. -- **Settles Changes to the Chain:** Commits batched changes back to Solana. +## Flow +1. **Fetches Transactions:** +2. **Delegates Funds:** Solana program +3. **Sends to the Sequencer:** +4. **Locks accounts, Loads, and executes Transactions:** +5. **Updates Local State:** +6. **Bundles Similar Transactions:** Groups similar transactions into one. +7. **Batch Bundling:** After 10 transactions, bundles them into a single unit. +8. **Settles Changes to the Chain:** Commits batched changes back to Solana. From 85a76a90c244efbd30ddb7b0b730f211b3a46a64 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:27:10 +0700 Subject: [PATCH 23/41] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 991c802..b93082c 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,11 @@ Rollups can enhance Solana by: - **Flexibility:** Allow for customized transaction processing without changing Solana’s core. ## Flow -1. **Fetches Transactions:** +1. **Fetches Transactions** 2. **Delegates Funds:** Solana program -3. **Sends to the Sequencer:** -4. **Locks accounts, Loads, and executes Transactions:** -5. **Updates Local State:** +3. **Sends to the Sequencer** +4. **Locks accounts, Loads, and executes Transactions** +5. **Updates Local State** 6. **Bundles Similar Transactions:** Groups similar transactions into one. 7. **Batch Bundling:** After 10 transactions, bundles them into a single unit. 8. **Settles Changes to the Chain:** Commits batched changes back to Solana. From 92efc1efda9c7072b91bcbb84446f0df51ca8e29 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:27:47 +0700 Subject: [PATCH 24/41] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b93082c..1c2929d 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,5 @@ Rollups can enhance Solana by: 4. **Locks accounts, Loads, and executes Transactions** 5. **Updates Local State** 6. **Bundles Similar Transactions:** Groups similar transactions into one. -7. **Batch Bundling:** After 10 transactions, bundles them into a single unit. +7. **Batch(10) Bundling:** After 10 transactions, bundles them into a single transaction. 8. **Settles Changes to the Chain:** Commits batched changes back to Solana. From a2cf546920853403036acfc1f83139b3bbcb6ae4 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:33:53 +0700 Subject: [PATCH 25/41] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1c2929d..f8ae6c4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ ## SVM rollup As part of the Turbin3 SVM cohort, our team built our own SVM rollup. +It fetches transactions, delegates funds via a Solana program, sends transactions to a sequencer, and processes them by locking accounts, loads and executes transactions, updating local state, and bundling similar transactions. Once a batch threshold is met (e.g., after 10 transactions), the rollup bundles them into one and settles the changes back on-chain. ## Overview A rollup is a Layer-2 scaling solution that processes and bundles transactions off-chain before settling them on the main chain. This reduces congestion and fees while keeping the security of the underlying blockchain. From c6bf5c1b461f7f7e7e242074c31f775b3850d8fb Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:44:25 +0700 Subject: [PATCH 26/41] Update README.md --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/README.md b/README.md index f8ae6c4..df9ce62 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,46 @@ Rollups can enhance Solana by: 6. **Bundles Similar Transactions:** Groups similar transactions into one. 7. **Batch(10) Bundling:** After 10 transactions, bundles them into a single transaction. 8. **Settles Changes to the Chain:** Commits batched changes back to Solana. + +## Module Overview + +**frontend.rs** + Actix Web + - A submission endpoint (`/submit_transaction`) that accepts and forwards transactions to the sequencer. + - A query endpoint (`/get_transaction`) that retrieves processed transactions from the rollup database. + - A test endpoint to verify server functionality. + +- **loader.rs** + Implements the account loader for the rollup. This module: + - Fetches account data from Solana using RPC client. + - Caches account data locally. + - Implements the `TransactionProcessingCallback` required by SVM API + +- **main.rs** + Entry point for the application. It: + - Sets up communication channels, using crossbeam and async channels. + - Creates threads for the sequencer and rollup database. + - Runs the Actix server, tying all modules together. + +- **processor.rs** + Provides helper functions to configure and initialize the SVM API’s transaction batch processor. +It: + - Implements a fork graph (required by the processor). + - Sets up the processor’s program cache with built-in programs (system and BPF loader). + +- **rollupdb.rs** + Implements an in-memory database that manages: + - Account states and locked accounts. + - Processed transactions. + - Communication with the frontend by retrieving transactions based on requests. + It handles locking and unlocking accounts as transactions are processed. + +- **sequencer.rs** + Acts as the transaction sequencer and processor. It: + - Receives transactions via a crossbeam channel. + - Locks accounts for parallel execution. + - Uses Solana’s SVM API to process and validate transactions. + - Batches transactions (every 10 transactions) and settles when the threshold is reached. + +- **settle.rs** + Contains the functionality to settle state changes on Solana. Creates and sends a proof transaction via Solana’s RPC, comitting updates to SVM. From cde452cd11f4ea878fc4c5c9b2afe3eda6708a69 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:45:08 +0700 Subject: [PATCH 27/41] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index df9ce62..64265a8 100644 --- a/README.md +++ b/README.md @@ -29,37 +29,37 @@ Rollups can enhance Solana by: - A query endpoint (`/get_transaction`) that retrieves processed transactions from the rollup database. - A test endpoint to verify server functionality. -- **loader.rs** +**loader.rs** Implements the account loader for the rollup. This module: - Fetches account data from Solana using RPC client. - Caches account data locally. - Implements the `TransactionProcessingCallback` required by SVM API -- **main.rs** +**main.rs** Entry point for the application. It: - Sets up communication channels, using crossbeam and async channels. - Creates threads for the sequencer and rollup database. - Runs the Actix server, tying all modules together. -- **processor.rs** +**processor.rs** Provides helper functions to configure and initialize the SVM API’s transaction batch processor. It: - Implements a fork graph (required by the processor). - Sets up the processor’s program cache with built-in programs (system and BPF loader). -- **rollupdb.rs** +**rollupdb.rs** Implements an in-memory database that manages: - Account states and locked accounts. - Processed transactions. - Communication with the frontend by retrieving transactions based on requests. It handles locking and unlocking accounts as transactions are processed. -- **sequencer.rs** +**sequencer.rs** Acts as the transaction sequencer and processor. It: - Receives transactions via a crossbeam channel. - Locks accounts for parallel execution. - Uses Solana’s SVM API to process and validate transactions. - Batches transactions (every 10 transactions) and settles when the threshold is reached. -- **settle.rs** +**settle.rs** Contains the functionality to settle state changes on Solana. Creates and sends a proof transaction via Solana’s RPC, comitting updates to SVM. From 2a1a841ef9b8723f2a3926b6d0e3b0167189b972 Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:46:41 +0700 Subject: [PATCH 28/41] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64265a8..f16cafd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## SVM rollup As part of the Turbin3 SVM cohort, our team built our own SVM rollup. -It fetches transactions, delegates funds via a Solana program, sends transactions to a sequencer, and processes them by locking accounts, loads and executes transactions, updating local state, and bundling similar transactions. Once a batch threshold is met (e.g., after 10 transactions), the rollup bundles them into one and settles the changes back on-chain. +It fetches transactions, delegates funds via a Solana program, sends transactions to a sequencer, and processes them by locking accounts, loads and executes transactions, updating local state, and bundling similar transactions. Once the batch threshold is met (10 transactions), the rollup bundles them into one and settles the changes back on-chain. ## Overview A rollup is a Layer-2 scaling solution that processes and bundles transactions off-chain before settling them on the main chain. This reduces congestion and fees while keeping the security of the underlying blockchain. From 3446c050b9631ae0a559f85b4f4985a97fe1657c Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:49:55 +0700 Subject: [PATCH 29/41] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index f16cafd..0f6fcbd 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ Rollups can enhance Solana by: ## Module Overview +**rollup_client.rs** +CLI that interacts with the rollup server. + - Creating and signing a Solana transfer transaction. + - Sends the transaction to the rollup via `/submit_transaction`. + - Hashes the transaction, via `/get_transaction` for status. + **frontend.rs** Actix Web - A submission endpoint (`/submit_transaction`) that accepts and forwards transactions to the sequencer. From d803cc1a615bdbc874594cfce2d998cf1e8434ab Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:51:55 +0700 Subject: [PATCH 30/41] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 0f6fcbd..29d779c 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,6 @@ CLI that interacts with the rollup server. Entry point for the application. It: - Sets up communication channels, using crossbeam and async channels. - Creates threads for the sequencer and rollup database. - - Runs the Actix server, tying all modules together. **processor.rs** Provides helper functions to configure and initialize the SVM API’s transaction batch processor. From 8d74e27c9e247d308d17668b37cccca6d29f3aae Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:53:41 +0700 Subject: [PATCH 31/41] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 29d779c..ffecdd2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ Rollups can enhance Solana by: - **Lower fees:** Batch transactions to lower costs. - **Flexibility:** Allow for customized transaction processing without changing Solana’s core. +## Currently building: +- **Support for SPL tokens** +- **Bundling between 3+ accounts** + ## Flow 1. **Fetches Transactions** 2. **Delegates Funds:** Solana program From f8c4b342650edf97e141566253c73ac530ec972a Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Tue, 18 Feb 2025 23:55:45 +0700 Subject: [PATCH 32/41] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffecdd2..a0a6e0a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Rollups can enhance Solana by: ## Currently building: - **Support for SPL tokens** -- **Bundling between 3+ accounts** +- **Support for all types of transactions** ## Flow 1. **Fetches Transactions** From bc2d15a92cd5af3927df2da7603f9d09974ed78f Mon Sep 17 00:00:00 2001 From: "w.sol" Date: Wed, 19 Feb 2025 00:01:09 +0700 Subject: [PATCH 33/41] Update README.md From 19f82e48a62162411977bbaa9537b7ff17a69ea6 Mon Sep 17 00:00:00 2001 From: "N.T. Williams" <62092476+NkamaWilliams@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:44:30 +0100 Subject: [PATCH 34/41] Update pk in owner.json --- rollup_client/owner.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rollup_client/owner.json b/rollup_client/owner.json index a0cfc48..e883fb4 100644 --- a/rollup_client/owner.json +++ b/rollup_client/owner.json @@ -1 +1 @@ -[243,134,232,182,38,20,221,84,184,243,148,63,174,105,39,42,60,164,154,188,238,42,142,55,204,80,197,171,93,205,219,35,168,72,163,61,183,44,236,227,199,179,145,171,70,76,237,85,10,59,99,187,157,143,17,4,64,119,14,33,111,249,24,237] \ No newline at end of file +[233,30,43,65,242,86,125,7,130,125,220,217,150,77,202,230,174,240,68,153,86,95,117,139,119,206,35,220,174,66,198,185,77,168,167,211,99,86,41,131,169,190,129,154,161,149,215,209,243,139,32,209,80,24,138,113,193,79,130,154,249,27,13,230] From 990d26066da067b9c4192070865f2cc5e3371632 Mon Sep 17 00:00:00 2001 From: timadigwe Date: Fri, 21 Feb 2025 14:48:26 +0100 Subject: [PATCH 35/41] anchor instruction --- rollup_client/src/main.rs | 78 +++++++++++++------------- rollup_core/src/delegation.rs | 34 ++++++++---- rollup_core/src/delegation_service.rs | 8 ++- rollup_core/src/frontend.rs | 56 ++++++------------- rollup_core/src/main.rs | 52 ++++------------- rollup_core/src/processor.rs | 59 +++++--------------- rollup_core/src/sequencer.rs | 80 +++++++++++++++++++-------- 7 files changed, 163 insertions(+), 204 deletions(-) diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index 467b400..5bcbbae 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -19,6 +19,7 @@ use std::{collections::HashMap, str::FromStr}; struct RollupTransaction { sender: String, sol_transaction: Transaction, + keypair_bytes: Vec, } #[derive(Serialize, Deserialize, Debug)] @@ -26,66 +27,61 @@ pub struct GetTransaction { pub get_tx: String, } +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "status", content = "data")] +pub enum TransactionResponse { + Success { message: String }, + Error { message: String }, +} + #[tokio::main] async fn main() -> Result<()> { - let keypair = signer::keypair::read_keypair_file("/Users/nicknut/Desktop/Q1_SVM/Rollup_SVM_Q1/Basic_Rollup_fork/rollup_client/mykey_1.json").unwrap(); - let keypair2 = signer::keypair::read_keypair_file("/Users/nicknut/Desktop/Q1_SVM/Rollup_SVM_Q1/Basic_Rollup_fork/rollup_client/testkey.json").unwrap(); + let keypair = signer::keypair::read_keypair_file("./mykey_1.json").unwrap(); + let keypair2 = signer::keypair::read_keypair_file("./testkey.json").unwrap(); let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); + let client = reqwest::Client::new(); + + println!("\nTesting delegation flow..."); - let ix = - system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), 1 * (LAMPORTS_PER_SOL/4)); + // 1. Create a transfer transaction + let transfer_amount = LAMPORTS_PER_SOL/4; + let ix = system_instruction::transfer( + &keypair2.pubkey(), + &keypair.pubkey(), + transfer_amount + ); + let tx = Transaction::new_signed_with_payer( &[ix], Some(&keypair2.pubkey()), &[&keypair2], - rpc_client.get_latest_blockhash().await.unwrap(), + rpc_client.get_latest_blockhash().await?, ); - // let sig = Signature::from_str("3ENa2e9TG6stDNkUZkRcC2Gf5saNMUFhpptQiNg56nGJ9eRBgSJpZBi7WLP5ev7aggG1JAXQWzBk8Xfkjcx1YCM2").unwrap(); - // let tx = rpc_client.get_transaction(&sig, UiTransactionEncoding::Binary).await.unwrap(); - let client = reqwest::Client::new(); - - // let tx_encoded: Transaction = tx.try_into().unwrap(); - - let test_response = client - .get("http://127.0.0.1:8080") - .send() - .await? - .json::>() - .await?; - - println!("{test_response:#?}"); - + // 2. Submit transaction to rollup let rtx = RollupTransaction { - sender: "Me".into(), + sender: keypair2.pubkey().to_string(), sol_transaction: tx, + keypair_bytes: keypair2.to_bytes().to_vec(), }; - // let serialized_rollup_transaction = serde_json::to_string(&rtx)?; - - let submit_transaction = client + let response = client .post("http://127.0.0.1:8080/submit_transaction") .json(&rtx) .send() + .await? + .json::() .await?; - // .json() - // .await?; - - println!("{submit_transaction:#?}"); - let mut hasher = Hasher::default(); - hasher.hash(bincode::serialize(&rtx.sol_transaction).unwrap().as_slice()); - - println!("{:#?}", hasher.clone().result()); - - let tx_resp = client - .post("http://127.0.0.1:8080/get_transaction") - .json(&HashMap::from([("get_tx", hasher.result().to_string())])) - .send() - .await?; - // .json::>() - // .await?; - println!("{tx_resp:#?}"); + // 3. Handle response + match response { + TransactionResponse::Success { message } => { + println!("Success: {}", message); + }, + TransactionResponse::Error { message } => { + println!("Error: {}", message); + } + } Ok(()) } diff --git a/rollup_core/src/delegation.rs b/rollup_core/src/delegation.rs index 333164c..f8c0c6c 100644 --- a/rollup_core/src/delegation.rs +++ b/rollup_core/src/delegation.rs @@ -5,12 +5,6 @@ use solana_sdk::{ }; use borsh::{BorshSerialize, BorshDeserialize}; -#[derive(BorshSerialize, BorshDeserialize)] -pub enum DelegationInstruction { - InitializeDelegate { amount: u64 }, - Withdraw { amount: u64 }, -} - #[derive(BorshSerialize, BorshDeserialize)] pub struct DelegatedAccount { pub owner: Pubkey, @@ -19,8 +13,19 @@ pub struct DelegatedAccount { pub bump: u8, } +#[derive(BorshSerialize)] +struct AnchorInstruction { + discriminator: [u8; 8], + data: InitializeDelegateArgs, +} + +#[derive(BorshSerialize)] +pub struct InitializeDelegateArgs { + pub amount: u64, +} + pub fn get_delegation_program_id() -> Pubkey { - "E1bxy4HwKFjPARhVe7NjvoFtynN69C4xNA53uSwruHrP" + "5MSF4TiUfD7dVm7P1ahPYJfEBLCUQn7hEPYXYHocVwzh" .parse() .unwrap() } @@ -32,12 +37,17 @@ pub fn find_delegation_pda(owner: &Pubkey) -> (Pubkey, u8) { ) } -pub fn create_delegation_instruction( - owner: &Pubkey, - amount: u64, -) -> Instruction { +pub fn create_delegation_instruction(owner: &Pubkey, amount: u64) -> Instruction { let (pda, _) = find_delegation_pda(owner); + // Anchor discriminator for "initialize_delegate" + let discriminator = [103, 117, 89, 87, 161, 37, 220, 226]; + + let ix_data = AnchorInstruction { + discriminator, + data: InitializeDelegateArgs { amount }, + }.try_to_vec().unwrap(); + Instruction { program_id: get_delegation_program_id(), accounts: vec![ @@ -45,6 +55,6 @@ pub fn create_delegation_instruction( AccountMeta::new(pda, false), AccountMeta::new_readonly(system_program::id(), false), ], - data: borsh::to_vec(&DelegationInstruction::InitializeDelegate { amount }).unwrap(), + data: ix_data, } } \ No newline at end of file diff --git a/rollup_core/src/delegation_service.rs b/rollup_core/src/delegation_service.rs index d4213f8..b582ec6 100644 --- a/rollup_core/src/delegation_service.rs +++ b/rollup_core/src/delegation_service.rs @@ -73,10 +73,14 @@ impl DelegationService { let instruction = create_delegation_instruction(user, amount); let recent_blockhash = self.rpc_client.get_latest_blockhash()?; - Ok(Transaction::new_with_payer( + // Create transaction with recent blockhash + let mut tx = Transaction::new_with_payer( &[instruction], Some(user), - )) + ); + tx.message.recent_blockhash = recent_blockhash; // Set the recent blockhash + + Ok(tx) } pub fn update_pda_state(&mut self, pda: Pubkey, account: AccountSharedData) { diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index 4d38f51..1979843 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -1,6 +1,7 @@ use std::{ collections::HashMap, sync::{Arc, Mutex}, + cell::RefCell, }; use actix_web::{error, web, HttpResponse}; @@ -33,56 +34,34 @@ pub struct GetTransaction { // message format used to receive transactions from clients #[derive(Serialize, Deserialize, Debug)] pub struct RollupTransaction { - sender: String, - sol_transaction: Transaction, + pub sender: String, + pub sol_transaction: Transaction, + pub keypair_bytes: Vec, } -#[derive(Serialize)] +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "status", content = "data")] pub enum TransactionResponse { Success { message: String }, - NeedsDelegation { delegation_tx: Transaction }, Error { message: String }, } +// Add thread local storage for keypair +thread_local! { + static KEYPAIR_STORAGE: RefCell>> = RefCell::new(None); +} + pub async fn submit_transaction( body: web::Json, - sequencer_sender: web::Data>, - delegation_service: web::Data>>, + sequencer_sender: web::Data)>>, ) -> actix_web::Result { - let tx = &body.sol_transaction; - let payer = tx.message.account_keys[0]; - let amount = 1_000_000; // Extract actual amount - - // Check delegation and get PDA - let delegation_result = delegation_service.write().unwrap() - .verify_delegation_for_transaction(&payer, amount) + // Send both transaction and keypair bytes + sequencer_sender.send((body.sol_transaction.clone(), body.keypair_bytes.clone())) .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; - match delegation_result { - Some(pda) => { - // Modify transaction to use PDA - let mut modified_tx = tx.clone(); - modified_tx.message.account_keys[0] = pda; - - // Send modified transaction to sequencer - sequencer_sender.send(modified_tx) - .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; - - Ok(HttpResponse::Ok().json(TransactionResponse::Success { - message: "Transaction submitted".to_string() - })) - } - None => { - // Create delegation transaction - let delegation_tx = delegation_service.write().unwrap() - .create_delegation_transaction(&payer, amount) - .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; - - Ok(HttpResponse::Ok().json(TransactionResponse::NeedsDelegation { - delegation_tx - })) - } - } + Ok(HttpResponse::Ok().json(TransactionResponse::Success { + message: "Transaction submitted".to_string() + })) } pub async fn get_transaction( @@ -111,6 +90,7 @@ pub async fn get_transaction( return Ok(HttpResponse::Ok().json(RollupTransaction { sender: "Rollup RPC".into(), sol_transaction: frontend_message.transaction.unwrap(), + keypair_bytes: Vec::new(), })); // Ok(HttpResponse::Ok().json(HashMap::from([("Transaction status", "requested")]))) } diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index d800f42..2ee4390 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -4,7 +4,7 @@ use crate::delegation_service::DelegationService; use actix_web::{web, App, HttpServer}; use async_channel; -use frontend::FrontendMessage; +use frontend::{FrontendMessage, RollupTransaction}; use rollupdb::{RollupDB, RollupDBMessage}; use solana_sdk::pubkey::Pubkey; use solana_sdk::{account::AccountSharedData, transaction::Transaction}; @@ -28,25 +28,16 @@ fn main() { // async log::info!("starting HTTP server at http://localhost:8080"); - // let (tx, rx) = oneshot::channel::>(); - let (sequencer_sender, sequencer_receiver) = crossbeam::channel::unbounded::(); + let (sequencer_sender, sequencer_receiver) = + crossbeam::channel::unbounded::<(Transaction, Vec)>(); let (rollupdb_sender, rollupdb_receiver) = crossbeam::channel::unbounded::(); -// let (sequencer_sender, sequencer_receiver) = async_channel::bounded::(100); -// let (rollupdb_sender, rollupdb_receiver) = async_channel::unbounded::(); + - // let (sequencer_sender, sequencer_receiver) = async_channel::bounded::(100); // Channel for communication between frontend and sequencer - // let (rollupdb_sender, rollupdb_receiver) = async_channel::unbounded::(); // Channel for communication between sequencer and accountsdb let (frontend_sender, frontend_receiver) = async_channel::unbounded::(); // Channel for communication between data availability layer and frontend pub type PubkeyAccountSharedData = Option>; let (account_sender, account_receiver) = async_channel::unbounded::(); let (sender_locked_account, receiver_locked_account) = async_channel::unbounded::(); - // std::thread::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); - - // let rt = Builder::new() - // .threaded_scheduler() - // .enable_all() - // .build() - // .unwrap(); + let db_sender2 = rollupdb_sender.clone(); let fe_2 = frontend_sender.clone(); @@ -54,6 +45,8 @@ fn main() { // async DelegationService::new("https://api.devnet.solana.com") )); + let delegation_service_clone = delegation_service.clone(); + let asdserver_thread = thread::spawn(|| { let rt = Builder::new_multi_thread() .worker_threads(4) @@ -66,19 +59,12 @@ fn main() { // async db_sender2, account_receiver, receiver_locked_account, + delegation_service_clone, ).await.unwrap() }); rt.block_on(RollupDB::run(rollupdb_receiver, fe_2, account_sender, sender_locked_account)); }); - // Create sequencer task - // tokio::spawn(sequencer::run(sequencer_receiver, rollupdb_sender.clone())); - // tokio::task::spawn_blocking(|| sequencer::run(sequencer_receiver, rollupdb_sender.clone()) ).await.unwrap(); - // tokio::task::block_in_place(|| sequencer::run(sequencer_receiver, rollupdb_sender.clone()) ).await.unwrap(); - - // Create rollup db task (accounts + transactions) - // tokio::spawn(RollupDB::run(rollupdb_receiver, frontend_sender.clone())); - - // let frontend_receiver_mutex = Arc::new(Mutex::new(frontend_receiver)); + // Spawn the Actix Web server in a separate thread let server_thread = thread::spawn(|| { @@ -96,20 +82,9 @@ fn main() { // async .app_data(web::Data::new(rollupdb_sender.clone())) .app_data(web::Data::new(frontend_sender.clone())) .app_data(web::Data::new(frontend_receiver.clone())) - .app_data(web::Data::new(delegation_service.clone())) .route("/", web::get().to(frontend::test)) - .route( - "/get_transaction", - web::post().to(frontend::get_transaction), - ) - .route( - "/submit_transaction", - web::post().to(frontend::submit_transaction), - ) - // .service( - // web::resource("/submit_transaction") - // .route(web::post().to(frontend::submit_transaction)), - // ) + .route("/get_transaction", web::post().to(frontend::get_transaction)) + .route("/submit_transaction", web::post().to(frontend::submit_transaction)) }) .worker_max_blocking_threads(2) .bind("127.0.0.1:8080") @@ -117,13 +92,8 @@ fn main() { // async .run() .await .unwrap(); - // tokio::time::sleep(std::time::Duration::from_secs(20)).await; }); }); server_thread.join().unwrap(); - // rt.shutdown_timeout(std::time::Duration::from_secs(20)); - - - // Ok(()) } diff --git a/rollup_core/src/processor.rs b/rollup_core/src/processor.rs index 37238f9..aa69c38 100644 --- a/rollup_core/src/processor.rs +++ b/rollup_core/src/processor.rs @@ -12,6 +12,7 @@ use { }, solana_system_program::system_processor, std::sync::{Arc, RwLock}, + std::collections::HashSet, }; /// In order to use the `TransactionBatchProcessor`, another trait - Solana @@ -37,61 +38,27 @@ pub(crate) fn create_transaction_batch_processor>, - // needed_programs: Vec, ) -> TransactionBatchProcessor { - // Create a new transaction batch processor. - // - // We're going to use slot 1 specifically because any programs we add will - // be deployed in slot 0, and they are delayed visibility until the next - // slot (1). - // This includes programs owned by BPF Loader v2, which are automatically - // marked as "depoyed" in slot 0. - // See `solana_svm::program_loader::program_with_pubkey` for more - // details. - let processor = TransactionBatchProcessor::::new_uninitialized( + let processor = TransactionBatchProcessor::::new( /* slot */ 1, /* epoch */ 1, - // Arc::downgrade(&fork_graph), - // Some(Arc::new( - // create_program_runtime_environment_v1(feature_set, compute_budget, false, false) - // .unwrap(), - // )), - // None, + HashSet::new(), ); processor.program_cache.write().unwrap().set_fork_graph(Arc::downgrade(&fork_graph)); - processor.prepare_program_cache_for_upcoming_feature_set(callbacks, feature_set, compute_budget, 1, 50); - - // processor.prepare_program_cache_for_upcoming_feature_set(callbacks, upcoming_feature_set, compute_budget, slot_index, slots_in_epoch); - - // Add the system program builtin. - processor.add_builtin( - callbacks, - solana_system_program::id(), - "system_program", - ProgramCacheEntry::new_builtin( - 0, - b"system_program".len(), - system_processor::Entrypoint::vm, - ), + processor.prepare_program_cache_for_upcoming_feature_set( + callbacks, feature_set, compute_budget, 1, 50 ); - // Add the BPF Loader v2 builtin, for the SPL Token program. + // Add system program processor.add_builtin( callbacks, - solana_sdk::bpf_loader::id(), - "solana_bpf_loader_program", - ProgramCacheEntry::new_builtin( - 0, - b"solana_bpf_loader_program".len(), - solana_bpf_loader_program::Entrypoint::vm, - ), + solana_system_program::id(), + "system_program", + ProgramCacheEntry::new_builtin(0, b"system_program".len(), system_processor::Entrypoint::vm), ); - // Adding any needed programs to the processor. - - processor } @@ -103,10 +70,10 @@ pub(crate) fn get_transaction_check_results( lamports_per_signature: u64, ) -> Vec> { vec![ - transaction::Result::Ok(CheckedTransactionDetails::new( - None, - lamports_per_signature - )); + transaction::Result::Ok(CheckedTransactionDetails { + nonce: None, + lamports_per_signature, + }); len ] } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 76a05d2..73d75f8 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -1,6 +1,6 @@ use core::panic; use std::{ - collections::{HashMap, HashSet}, sync::{Arc, RwLock}, time, vec + collections::{HashMap, HashSet}, sync::{Arc, RwLock}, time, vec, cell::RefCell }; use anyhow::{anyhow, Result}; @@ -15,11 +15,17 @@ use solana_program_runtime::{ use solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1; use solana_sdk::{ - account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, instruction, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, sysvar::instructions, transaction::{SanitizedTransaction, Transaction}, transaction_context::{IndexOfAccount, TransactionContext}, + account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, instruction, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, signature::Keypair, sysvar::instructions, transaction::{SanitizedTransaction, Transaction}, transaction_context::{IndexOfAccount, TransactionContext} }; use solana_timings::ExecuteTimings; use solana_svm::{ -transaction_processing_callback::TransactionProcessingCallback, transaction_processing_result::ProcessedTransaction, transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment} + transaction_processing_callback::TransactionProcessingCallback, + transaction_processor::{ + TransactionBatchProcessor, + TransactionProcessingConfig, + TransactionProcessingEnvironment, + LoadAndExecuteSanitizedTransactionsOutput, + } }; use tokio::time::{sleep, Duration}; use crate::{rollupdb::RollupDBMessage, settle::settle_state}; @@ -27,12 +33,16 @@ use crate::loader::RollupAccountLoader; use crate::processor::*; use crate::errors::RollupErrors; use crate::delegation_service::DelegationService; +use crate::delegation::{find_delegation_pda}; + + pub async fn run( - sequencer_receiver_channel: CBReceiver, + sequencer_receiver_channel: CBReceiver<(Transaction, Vec)>, rollupdb_sender: CBSender, account_reciever: Receiver>>, receiver_locked_accounts: Receiver, + delegation_service: Arc>, ) -> Result<()> { let mut tx_counter = 0u32; @@ -41,7 +51,38 @@ pub async fn run( let mut rollup_account_loader = RollupAccountLoader::new( &rpc_client_temp, ); - while let transaction = sequencer_receiver_channel.recv().unwrap() { + while let (transaction, keypair_bytes) = sequencer_receiver_channel.recv().unwrap() { + let payer = transaction.message.account_keys[0]; + let amount = 1_000_000; // Replace with actual amount extraction + + // Check delegation + let delegation_result = delegation_service.write().unwrap() + .verify_delegation_for_transaction(&payer, amount)?; + + if delegation_result.is_none() { + let mut delegation_tx = delegation_service.write().unwrap() + .create_delegation_transaction(&payer, amount)?; + + // Get keypair from storage + let keypair = Keypair::from_bytes(&keypair_bytes)?; + + delegation_tx.sign(&[&keypair], delegation_tx.message.recent_blockhash); + + // Submit delegation transaction to network + let sig = rpc_client_temp.send_and_confirm_transaction(&delegation_tx)?; + log::info!("Created delegation with signature: {}", sig); + + // Wait for delegation to be confirmed + tokio::time::sleep(Duration::from_secs(2)).await; + + // Clear cache to force refresh of delegation state + let (pda, _) = find_delegation_pda(&payer); + if let Ok(account) = rpc_client_temp.get_account(&pda) { + delegation_service.write().unwrap() + .update_pda_state(pda, account.into()); + } + } + let accounts_to_lock = transaction.message.account_keys.clone(); for pubkey in accounts_to_lock.iter() { loop { @@ -106,7 +147,6 @@ pub async fn run( log::info!("{:?}", sanitized.clone()); - let payer = transaction.message.account_keys[0]; let amount = 1_000_000; // For now, using a fixed amount. Replace with actual amount extraction let processor = create_transaction_batch_processor( @@ -121,11 +161,12 @@ pub async fn run( let processing_environment = TransactionProcessingEnvironment { blockhash: Hash::default(), - epoch_total_stake: 0u64, + epoch_total_stake: Some(0u64), feature_set: Arc::new(feature_set), rent_collector: Some(&rent_collector), - blockhash_lamports_per_signature: fee_structure.lamports_per_signature, - fee_lamports_per_signature: fee_structure.lamports_per_signature, + epoch_vote_accounts: Some(&HashMap::new()), + fee_structure: Some(&fee_structure), + lamports_per_signature: fee_structure.lamports_per_signature, }; let processing_config = TransactionProcessingConfig { @@ -140,28 +181,19 @@ pub async fn run( &processing_environment, &processing_config ); - log::info!("{:#?}", status.processing_results); + log::info!("{:#?}", status.execution_results); log::info!("error_metrics: {:#?}", status.error_metrics); let data_new = status - .processing_results + .loaded_transactions // Use loaded_transactions instead .iter() - .map(|res| { + .map(|tx| { println!("Executed transaction:"); log::info!("Executed transaction"); - let enum_one = res.as_ref().unwrap(); - - match enum_one { - ProcessedTransaction::Executed(tx) => { - println!("Executed transaction: {:?}", tx.loaded_transaction.accounts); - Some(tx.loaded_transaction.accounts.clone()) - } - ProcessedTransaction::FeesOnly(tx) => { - println!("Fees-only transaction: {:?}", tx); - None - } - } + Some(tx.accounts.iter() + .map(|(pubkey, account)| (*pubkey, account.clone())) + .collect()) }).collect::>>>(); let first_index_data = data_new[0].as_ref().unwrap().clone(); From 714a47c4af0a95bddbde158545fa3ab233cc3bb5 Mon Sep 17 00:00:00 2001 From: timadigwe Date: Mon, 24 Feb 2025 16:32:56 +0100 Subject: [PATCH 36/41] updated logic --- rollup_core/Cargo.toml | 5 +- rollup_core/src/delegation_service.rs | 18 +- rollup_core/src/main.rs | 27 +- rollup_core/src/processor.rs | 17 +- rollup_core/src/sequencer.rs | 381 +++++++++++++++++++++----- 5 files changed, 363 insertions(+), 85 deletions(-) diff --git a/rollup_core/Cargo.toml b/rollup_core/Cargo.toml index 17e4c4f..362545b 100644 --- a/rollup_core/Cargo.toml +++ b/rollup_core/Cargo.toml @@ -22,4 +22,7 @@ log = "0.4.22" anyhow = "1.0.86" crossbeam = "0.8.4" async-channel = "2.3.1" -borsh = "0.10" \ No newline at end of file +bincode = "1.3.3" +borsh = "0.10" +sha1 = "=0.10.0" +digest = "=0.10.7" \ No newline at end of file diff --git a/rollup_core/src/delegation_service.rs b/rollup_core/src/delegation_service.rs index b582ec6..440d00e 100644 --- a/rollup_core/src/delegation_service.rs +++ b/rollup_core/src/delegation_service.rs @@ -1,27 +1,21 @@ use { - crate::delegation::{find_delegation_pda, DelegatedAccount, create_delegation_instruction}, - solana_sdk::{ - account::{AccountSharedData, ReadableAccount}, - pubkey::Pubkey, - transaction::Transaction, - message::Message, - }, - solana_client::rpc_client::RpcClient, - anyhow::{Result, anyhow}, - std::collections::HashMap, - borsh::BorshDeserialize, + crate::delegation::{create_delegation_instruction, find_delegation_pda, DelegatedAccount}, anyhow::{anyhow, Result}, borsh::BorshDeserialize, solana_client::rpc_client::RpcClient, solana_sdk::{ + account::{AccountSharedData, ReadableAccount}, message::Message, pubkey::Pubkey, signature::Keypair, transaction::Transaction + }, std::collections::HashMap }; pub struct DelegationService { rpc_client: RpcClient, pda_cache: HashMap, + signer: Keypair, } impl DelegationService { - pub fn new(rpc_url: &str) -> Self { + pub fn new(rpc_url: &str, signer: Keypair) -> Self { Self { rpc_client: RpcClient::new(rpc_url.to_string()), pda_cache: HashMap::new(), + signer: signer, } } diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 2ee4390..6879cee 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -2,11 +2,12 @@ use std::thread; use std::sync::{Arc, RwLock}; use crate::delegation_service::DelegationService; -use actix_web::{web, App, HttpServer}; +use actix_web::{web, App, HttpResponse, HttpServer}; use async_channel; use frontend::{FrontendMessage, RollupTransaction}; use rollupdb::{RollupDB, RollupDBMessage}; use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::Keypair; use solana_sdk::{account::AccountSharedData, transaction::Transaction}; use tokio::runtime::Builder; use tokio::sync::oneshot; @@ -29,7 +30,7 @@ fn main() { // async log::info!("starting HTTP server at http://localhost:8080"); let (sequencer_sender, sequencer_receiver) = - crossbeam::channel::unbounded::<(Transaction, Vec)>(); + crossbeam::channel::unbounded::(); let (rollupdb_sender, rollupdb_receiver) = crossbeam::channel::unbounded::(); @@ -40,12 +41,18 @@ fn main() { // async let db_sender2 = rollupdb_sender.clone(); let fe_2 = frontend_sender.clone(); + + + let signer = Keypair::new(); // Temporary keypair, will be replaced when client connects + + let (delegation_keypair_sender, delegation_keypair_receiver) = async_channel::unbounded::>(); let delegation_service = Arc::new(RwLock::new( - DelegationService::new("https://api.devnet.solana.com") + DelegationService::new("https://api.devnet.solana.com", signer) )); - + let delegation_service_clone = delegation_service.clone(); + let asdserver_thread = thread::spawn(|| { let rt = Builder::new_multi_thread() @@ -85,6 +92,18 @@ fn main() { // async .route("/", web::get().to(frontend::test)) .route("/get_transaction", web::post().to(frontend::get_transaction)) .route("/submit_transaction", web::post().to(frontend::submit_transaction)) + .route( + "/init_delegation_service", + { + let delegation_service = delegation_service.clone(); + web::post().to(move |body: web::Bytes| { + let keypair = Keypair::from_bytes(&body).unwrap(); + *delegation_service.write().unwrap() = + DelegationService::new("https://api.devnet.solana.com", keypair); + HttpResponse::Ok() + }) + }, + ) }) .worker_max_blocking_threads(2) .bind("127.0.0.1:8080") diff --git a/rollup_core/src/processor.rs b/rollup_core/src/processor.rs index aa69c38..e5beeb7 100644 --- a/rollup_core/src/processor.rs +++ b/rollup_core/src/processor.rs @@ -41,8 +41,13 @@ pub(crate) fn create_transaction_batch_processor TransactionBatchProcessor { let processor = TransactionBatchProcessor::::new( /* slot */ 1, - /* epoch */ 1, - HashSet::new(), + /* epoch */ 1, + Arc::downgrade(&fork_graph), + Some(Arc::new( + create_program_runtime_environment_v1(feature_set, compute_budget, false, false) + .unwrap(), + )), + None, ); processor.program_cache.write().unwrap().set_fork_graph(Arc::downgrade(&fork_graph)); @@ -70,10 +75,10 @@ pub(crate) fn get_transaction_check_results( lamports_per_signature: u64, ) -> Vec> { vec![ - transaction::Result::Ok(CheckedTransactionDetails { - nonce: None, - lamports_per_signature, - }); + transaction::Result::Ok(CheckedTransactionDetails::new( + None, + lamports_per_signature + )); len ] } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 73d75f8..a3b2f67 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -1,6 +1,6 @@ use core::panic; use std::{ - collections::{HashMap, HashSet}, sync::{Arc, RwLock}, time, vec, cell::RefCell + collections::{HashMap, HashSet}, sync::{Arc, RwLock}, time, vec }; use anyhow::{anyhow, Result}; @@ -15,35 +15,28 @@ use solana_program_runtime::{ use solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1; use solana_sdk::{ - account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, instruction, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, signature::Keypair, sysvar::instructions, transaction::{SanitizedTransaction, Transaction}, transaction_context::{IndexOfAccount, TransactionContext} + account::{AccountSharedData, ReadableAccount}, clock::{Epoch, Slot}, feature_set::FeatureSet, fee::FeeStructure, hash::Hash, instruction, pubkey::Pubkey, rent::Rent, rent_collector::RentCollector, sysvar::instructions, transaction::{SanitizedTransaction, Transaction}, transaction_context::{IndexOfAccount, TransactionContext}, }; use solana_timings::ExecuteTimings; use solana_svm::{ - transaction_processing_callback::TransactionProcessingCallback, - transaction_processor::{ - TransactionBatchProcessor, - TransactionProcessingConfig, - TransactionProcessingEnvironment, - LoadAndExecuteSanitizedTransactionsOutput, - } + transaction_processing_callback::TransactionProcessingCallback, transaction_processing_result::ProcessedTransaction, transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment} }; use tokio::time::{sleep, Duration}; -use crate::{rollupdb::RollupDBMessage, settle::settle_state}; +use crate::{delegation_service::DelegationService, rollupdb::RollupDBMessage, settle::settle_state}; use crate::loader::RollupAccountLoader; use crate::processor::*; use crate::errors::RollupErrors; -use crate::delegation_service::DelegationService; -use crate::delegation::{find_delegation_pda}; - - -pub async fn run( - sequencer_receiver_channel: CBReceiver<(Transaction, Vec)>, - rollupdb_sender: CBSender, +pub async fn run( // async + sequencer_receiver_channel: CBReceiver, // CBReceiver + rollupdb_sender: CBSender, // CBSender account_reciever: Receiver>>, receiver_locked_accounts: Receiver, delegation_service: Arc>, + // rx: tokio::sync::oneshot::Receiver> // sync_ver_sender ) -> Result<()> { + // let (tx, rx) = oneshot::channel(); // Create a channel to wait for response + let mut tx_counter = 0u32; let rpc_client_temp = RpcClient::new("https://api.devnet.solana.com".to_string()); @@ -51,38 +44,7 @@ pub async fn run( let mut rollup_account_loader = RollupAccountLoader::new( &rpc_client_temp, ); - while let (transaction, keypair_bytes) = sequencer_receiver_channel.recv().unwrap() { - let payer = transaction.message.account_keys[0]; - let amount = 1_000_000; // Replace with actual amount extraction - - // Check delegation - let delegation_result = delegation_service.write().unwrap() - .verify_delegation_for_transaction(&payer, amount)?; - - if delegation_result.is_none() { - let mut delegation_tx = delegation_service.write().unwrap() - .create_delegation_transaction(&payer, amount)?; - - // Get keypair from storage - let keypair = Keypair::from_bytes(&keypair_bytes)?; - - delegation_tx.sign(&[&keypair], delegation_tx.message.recent_blockhash); - - // Submit delegation transaction to network - let sig = rpc_client_temp.send_and_confirm_transaction(&delegation_tx)?; - log::info!("Created delegation with signature: {}", sig); - - // Wait for delegation to be confirmed - tokio::time::sleep(Duration::from_secs(2)).await; - - // Clear cache to force refresh of delegation state - let (pda, _) = find_delegation_pda(&payer); - if let Ok(account) = rpc_client_temp.get_account(&pda) { - delegation_service.write().unwrap() - .update_pda_state(pda, account.into()); - } - } - + while let transaction = sequencer_receiver_channel.recv().unwrap() { let accounts_to_lock = transaction.message.account_keys.clone(); for pubkey in accounts_to_lock.iter() { loop { @@ -147,7 +109,13 @@ pub async fn run( log::info!("{:?}", sanitized.clone()); - let amount = 1_000_000; // For now, using a fixed amount. Replace with actual amount extraction + // let needed_programs: Vec<(Pubkey, AccountSharedData)> = + // accounts_data + // .iter() + // .filter(|(pubkey, account)| account.executable()) + // .map(|(pubkey, account)| (pubkey.clone(), account.clone())) + // .collect(); + let processor = create_transaction_batch_processor( &rollup_account_loader, @@ -161,12 +129,14 @@ pub async fn run( let processing_environment = TransactionProcessingEnvironment { blockhash: Hash::default(), - epoch_total_stake: Some(0u64), + epoch_total_stake: 0u64, + //epoch_vote_accounts: None, feature_set: Arc::new(feature_set), + //fee_structure: Some(&fee_structure), + //lamports_per_signature: fee_structure.lamports_per_signature, + blockhash_lamports_per_signature: fee_structure.lamports_per_signature, + fee_lamports_per_signature: fee_structure.lamports_per_signature, rent_collector: Some(&rent_collector), - epoch_vote_accounts: Some(&HashMap::new()), - fee_structure: Some(&fee_structure), - lamports_per_signature: fee_structure.lamports_per_signature, }; let processing_config = TransactionProcessingConfig { @@ -181,19 +151,28 @@ pub async fn run( &processing_environment, &processing_config ); - log::info!("{:#?}", status.execution_results); + log::info!("{:#?}", status.processing_results); log::info!("error_metrics: {:#?}", status.error_metrics); let data_new = status - .loaded_transactions // Use loaded_transactions instead + .processing_results .iter() - .map(|tx| { + .map(|res| { println!("Executed transaction:"); log::info!("Executed transaction"); - Some(tx.accounts.iter() - .map(|(pubkey, account)| (*pubkey, account.clone())) - .collect()) + let enum_one = res.as_ref().unwrap(); + + match enum_one { + ProcessedTransaction::Executed(tx) => { + println!("Executed transaction: {:?}", tx.loaded_transaction.accounts); + Some(tx.loaded_transaction.accounts.clone()) + } + ProcessedTransaction::FeesOnly(tx) => { + println!("Fees-only transaction: {:?}", tx); + None + } + } }).collect::>>>(); let first_index_data = data_new[0].as_ref().unwrap().clone(); @@ -202,7 +181,7 @@ pub async fn run( rollupdb_sender .send(RollupDBMessage { lock_accounts: None, - add_processed_transaction: Some(transaction), + add_processed_transaction: Some(transaction.clone()), add_new_data: Some(first_index_data), frontend_get_tx: None, add_settle_proof: None, @@ -211,10 +190,288 @@ pub async fn run( .unwrap(); + + // Call settle if transaction amount since last settle hits 10 if tx_counter >= 10 { - tx_counter = 0u32; + log::info!("Start bundling!"); + //bundle transfer tx test + rollupdb_sender.send(RollupDBMessage { + lock_accounts: None, + add_processed_transaction: None, + add_settle_proof: None, + get_account: None, + add_new_data: None, + frontend_get_tx: None, + }).unwrap(); } } Ok(()) } + + // Lock db to avoid state changes during settlement + + // Prepare root hash, or your own proof to send to chain + + // Send proof to chain + + // let _settle_tx_hash = settle_state("proof".into()).await?; + // .map(|pubkey| { + // ( + // pubkey.clone(), + // rpc_client_temp.get_account(pubkey).unwrap().into(), + // ) + // }) + + +// log::info!("accounts_data: {needed_programs:?}"); + + // for (pubkey, account) in needed_programs.iter() { + // rollup_account_loader.add_account(*pubkey, account.clone()); + // } + +// let rent_collector = RentCollector::default(); + + // Solana runtime. + // let fork_graph = Arc::new(RwLock::new(SequencerForkGraph {})); + + // // create transaction processor, add accounts and programs, builtins, + // let processor = TransactionBatchProcessor::::default(); + + // let mut cache = processor.program_cache.write().unwrap(); + + // // Initialize the mocked fork graph. + // // let fork_graph = Arc::new(RwLock::new(PayTubeForkGraph {})); + // cache.fork_graph = Some(Arc::downgrade(&fork_graph)); + + // let rent = Rent::default(); + + + + // //****************************************************************************************************/ + // // let instructions = &transaction.message.instructions; + // // // let index_array_of_program_pubkeys = Vec::with_capacity(instructions.len()); + // // let program_ids = &transaction.message.account_keys; + + // // let needed_programs: Vec<&Pubkey> = instructions + // // .iter() + // // .map( + // // |instruction| + // // instruction.program_id(program_ids)).collect(); + // //****************************************************************************************************/ + + // let mut transaction_context = TransactionContext::new( + // accounts_data, + // Rent::default(), + // compute_budget.max_instruction_stack_depth, + // compute_budget.max_instruction_trace_length, + // ); + // // transaction_context.get_current_instruction_context().unwrap().get_index_of_program_account_in_transaction(2).unwrap(); + // // transaction_context.push(); + + + // // here we have to load them somehow + + // let runtime_env = Arc::new( + // create_program_runtime_environment_v1(&feature_set, &compute_budget, false, false) + // .unwrap(), + // ); + + // let mut prog_cache = ProgramCacheForTxBatch::new( + // Slot::default(), + // ProgramRuntimeEnvironments { + // program_runtime_v1: runtime_env.clone(), + // program_runtime_v2: runtime_env, + // }, + // None, + // Epoch::default(), + // ); + + + // // prog_cache.replenish(accounts_data., entry) + + // let sysvar_c = sysvar_cache::SysvarCache::default(); + // let env = EnvironmentConfig::new( + // Hash::default(), + // None, + // None, + // Arc::new(feature_set), + // lamports_per_signature, + // &sysvar_c, + // ); + // // let default_env = EnvironmentConfig::new(blockhash, epoch_total_stake, epoch_vote_accounts, feature_set, lamports_per_signature, sysvar_cache) + + // // let processing_environment = TransactionProcessingEnvironment { + // // blockhash: Hash::default(), + // // epoch_total_stake: None, + // // epoch_vote_accounts: None, + // // feature_set: Arc::new(feature_set), + // // fee_structure: Some(&fee_structure), + // // lamports_per_signature, + // // rent_collector: Some(&rent_collector), + // // }; + + + + // // for (pubkey, account) in rollup_account_loader.cache.read().unwrap().iter() { + // // let _p = rollup_account_loader.get_account_shared_data(pubkey); + // // log::info!("account: {_p:?}"); + // // } + // // let cache = &rollup_account_loader.cache.read().unwrap(); + // // let pew = cache.keys().next().cloned().unwrap(); + // // let owner = cache.get(&pew).unwrap().owner(); + // // log::debug!("pubkey: {owner:?}"); + + + // let program_cache_entry = load_program_with_pubkey( + // &rollup_account_loader, + // &prog_cache.environments, + // &rollup_account_loader.cache.read().unwrap().keys().next().cloned().unwrap(),//&needed_programs[0].0, + // 0, + // &mut ExecuteTimings::default(), + // false + // ); + + // log::info!("program_cache_entry: {program_cache_entry:?}"); + + // prog_cache.replenish( + // needed_programs[0].0, + // program_cache_entry.unwrap(), + // ); + // // { + // // let instruction_ctx = transaction_context.get_current_instruction_context(); + // // log::debug!("instruction_ctx: {instruction_ctx:?}"); + // // } + // // let instruction_ctx_height = transaction_context.get_instruction_context_stack_height(); + + // // log::debug!("instruction_ctx_height: {instruction_ctx_height}"); + + // // let instruction_ctx_next = transaction_context.get_next_instruction_context(); + // // // let instruction_ctx = transaction_context.get_next_instruction_context(); + + // // log::debug!("instruction_ctx: {instruction_ctx_next:?}"); + + + + // let mut invoke_context = InvokeContext::new( + // &mut transaction_context, + // &mut prog_cache, + // env, + // None, + // compute_budget.to_owned() + // ); + + + // // let instruction_ctx_2 = invoke_context.transaction_context.get_current_instruction_context(); + // // log::debug!("instruction_ctx_2: {instruction_ctx_2:?}"); + // // let instruction_ctx_height = invoke_context.transaction_context.get_instruction_context_stack_height(); + // // log::debug!("instruction_ctx_height: {instruction_ctx_height}"); + // // let instruction_ctx_height = invoke_context.transaction_context.get_instruction_context_at_index_in_trace(0); + // // log::debug!("instruction_ctx_height: {instruction_ctx_height:?}"); + + + + + // // HAS TO BE AN ADDRESS OF THE PROGRAM + + // // invoke_context.program_cache_for_tx_batch.replenish(key, program_cache_entry.unwrap()); + + + + + + + + // // let account_index = invoke_context + // // .transaction_context + // // .find_index_of_account(&instructions::id()); + + // // if account_index.is_none() { + // // panic!("Could not find instructions account"); + // // } + + // let program_indices: Vec = vec![0]; + // let result_msg = MessageProcessor::process_message( + // &sanitized.unwrap().message().to_owned(), // ERROR WITH SOLANA_SVM VERSION + // // ?should be fixed with help of chagning versions of solana-svm ? + // // &sanitized.unwrap().message().to_owned(), + // &[program_indices], // TODO: automotize this process + // &mut invoke_context, + // &mut timings, + // &mut used_cu, + // ); + + // log::info!("{:?}", &result_msg); + // log::info!("The message was done sucessfully"); + + + + + + +// TWO WAYS -> TRANSACTIONBATCHPROCCESOR OR MESSAGEPROCESSOR + +// PAYTUBE in SVM FOLDER + +// The question of how often to pull/push the state out of mainnet state + +// PDA as a *treasury , to solve problem with sol that could disapear from account + +// to create kind of a program that will lock funds on mainnet + +// MagicBlock relyaing on their infrustructure + +// To make a buffer between sending two transactions + + + + +// / In order to use the `TransactionBatchProcessor`, another trait - Solana +// / Program Runtime's `ForkGraph` - must be implemented, to tell the batch +// / processor how to work across forks. +// / +// /// Since our rollup doesn't use slots or forks, this implementation is mocked. +// pub(crate) struct SequencerForkGraph {} + +// impl ForkGraph for SequencerForkGraph { +// fn relationship(&self, _a: Slot, _b: Slot) -> BlockRelation { +// BlockRelation::Unknown +// } +// } +// pub struct SequencerAccountLoader<'a> { +// cache: RwLock>, +// rpc_client: &'a RpcClient, +// } + +// impl<'a> SequencerAccountLoader<'a> { +// pub fn new(rpc_client: &'a RpcClient) -> Self { +// Self { +// cache: RwLock::new(HashMap::new()), +// rpc_client, +// } +// } +// } + +// / Implementation of the SVM API's `TransactionProcessingCallback` interface. +// / +// / The SVM API requires this plugin be provided to provide the SVM with the +// / ability to load accounts. +// / +// / In the Agave validator, this implementation is Bank, powered by AccountsDB. +// impl TransactionProcessingCallback for SequencerAccountLoader<'_> { +// fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option { +// if let Some(account) = self.cache.read().unwrap().get(pubkey) { +// return Some(account.clone()); +// } + +// let account: AccountSharedData = self.rpc_client.get_account(pubkey).ok()?.into(); +// self.cache.write().unwrap().insert(*pubkey, account.clone()); + +// Some(account) +// } + +// fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option { +// self.get_account_shared_data(account) +// .and_then(|account| owners.iter().position(|key| account.owner().eq(key))) +// } +// } \ No newline at end of file From e9043d8a0381ac273a029a9c47d0bbcd6c7b9592 Mon Sep 17 00:00:00 2001 From: timadigwe Date: Wed, 26 Feb 2025 12:33:33 +0100 Subject: [PATCH 37/41] anchor error fix --- rollup_client/keys/receiver.json | 1 + rollup_client/keys/sender.json | Bin 0 -> 64 bytes rollup_client/src/bin/generate_keys.rs | 34 ++++++++++ rollup_client/src/main.rs | 82 ++++++++++++++----------- rollup_core/Cargo.toml | 3 +- rollup_core/src/delegation.rs | 44 +++++++++---- rollup_core/src/delegation_service.rs | 76 ++++++++++++++++------- rollup_core/src/frontend.rs | 6 +- rollup_core/src/main.rs | 15 ++++- rollup_core/src/sequencer.rs | 59 +++++++++++++++++- 10 files changed, 241 insertions(+), 79 deletions(-) create mode 100644 rollup_client/keys/receiver.json create mode 100644 rollup_client/keys/sender.json create mode 100644 rollup_client/src/bin/generate_keys.rs diff --git a/rollup_client/keys/receiver.json b/rollup_client/keys/receiver.json new file mode 100644 index 0000000..6a0be4c --- /dev/null +++ b/rollup_client/keys/receiver.json @@ -0,0 +1 @@ +›Ë½õÿSœS=ÊG«iÜ?v¢¿£ƒÙsÖg°ëúXwäÊÒ·ÜOúÃh3÷é7µØ-Uw™K \ No newline at end of file diff --git a/rollup_client/keys/sender.json b/rollup_client/keys/sender.json new file mode 100644 index 0000000000000000000000000000000000000000..2200dcdbb484405563ecdea595bef2945bd59efc GIT binary patch literal 64 zcmV-G0KfkLdYy!}AGDWwhp(Z!h_(qlluaQ1X?YI9Mt@kx!l25CC=utW5r+Sd4#5g| WlrLedn21qjf6z9eOncqSKwJ0m=O3N` literal 0 HcmV?d00001 diff --git a/rollup_client/src/bin/generate_keys.rs b/rollup_client/src/bin/generate_keys.rs new file mode 100644 index 0000000..cc964f9 --- /dev/null +++ b/rollup_client/src/bin/generate_keys.rs @@ -0,0 +1,34 @@ +use solana_sdk::{ + signature::Keypair, + signer::Signer, +}; +use std::fs; +use std::path::Path; + +fn main() { + // Create keypairs + let sender = Keypair::new(); + let receiver = Keypair::new(); + + // Create keys directory if it doesn't exist + let keys_dir = Path::new("keys"); + fs::create_dir_all(keys_dir).unwrap(); + + // Save sender keypair + fs::write( + keys_dir.join("sender.json"), + sender.to_bytes().to_vec() + ).unwrap(); + + // Save receiver keypair + fs::write( + keys_dir.join("receiver.json"), + receiver.to_bytes().to_vec() + ).unwrap(); + + // Print public keys + println!("Generated keypairs:"); + println!("Sender pubkey: {} (saved to keys/sender.json)", sender.pubkey()); + println!("Receiver pubkey: {} (saved to keys/receiver.json)", receiver.pubkey()); + println!("\nPlease airdrop SOL to these addresses using the Solana CLI or devnet faucet"); +} \ No newline at end of file diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index 5bcbbae..febac61 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -6,13 +6,13 @@ use solana_sdk::{ instruction::Instruction, hash::{Hash, Hasher}, native_token::LAMPORTS_PER_SOL, - signature::Signature, - signer::{self, Signer}, + signature::{Keypair, Signer}, system_instruction, system_program, transaction::Transaction, + pubkey::Pubkey, }; use solana_transaction_status::UiTransactionEncoding::{self, Binary}; -use std::{collections::HashMap, str::FromStr}; +use std::{collections::HashMap, str::FromStr, time::Duration, fs}; // use serde_json; #[derive(Serialize, Deserialize, Debug)] @@ -35,53 +35,61 @@ pub enum TransactionResponse { } #[tokio::main] -async fn main() -> Result<()> { - let keypair = signer::keypair::read_keypair_file("./mykey_1.json").unwrap(); - let keypair2 = signer::keypair::read_keypair_file("./testkey.json").unwrap(); - let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); - let client = reqwest::Client::new(); - - println!("\nTesting delegation flow..."); +async fn main() -> Result<(), Box> { + // Load existing keypairs from files + let sender = Keypair::from_bytes(&fs::read("keys/sender.json")?)?; + let receiver = Keypair::from_bytes(&fs::read("keys/receiver.json")?)?; + + // Connect to devnet + let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string()); + + // Print balances + let sender_balance = rpc_client.get_balance(&sender.pubkey()).await?; + let receiver_balance = rpc_client.get_balance(&receiver.pubkey()).await?; + println!("Sender {} balance: {} SOL", sender.pubkey(), sender_balance as f64 / 1_000_000_000.0); + println!("Receiver {} balance: {} SOL", receiver.pubkey(), receiver_balance as f64 / 1_000_000_000.0); - // 1. Create a transfer transaction - let transfer_amount = LAMPORTS_PER_SOL/4; - let ix = system_instruction::transfer( - &keypair2.pubkey(), - &keypair.pubkey(), - transfer_amount - ); + // Initialize delegation service + println!("\nInitializing delegation service..."); + let client = reqwest::Client::new(); + let response = client + .post("http://127.0.0.1:8080/init_delegation_service") + .body(sender.to_bytes().to_vec()) + .send() + .await?; + + println!("Delegation service init response: {:?}", response.text().await?); - let tx = Transaction::new_signed_with_payer( - &[ix], - Some(&keypair2.pubkey()), - &[&keypair2], + // Wait for initialization + tokio::time::sleep(Duration::from_secs(2)).await; + + // Create test transaction + let test_tx = Transaction::new_signed_with_payer( + &[system_instruction::transfer( + &sender.pubkey(), + &receiver.pubkey(), + 250_000_000, // 0.25 SOL + )], + Some(&sender.pubkey()), + &[&sender], rpc_client.get_latest_blockhash().await?, ); - // 2. Submit transaction to rollup + // Create RollupTransaction let rtx = RollupTransaction { - sender: keypair2.pubkey().to_string(), - sol_transaction: tx, - keypair_bytes: keypair2.to_bytes().to_vec(), + sender: sender.pubkey().to_string(), + sol_transaction: test_tx, + keypair_bytes: sender.to_bytes().to_vec(), }; + // Send to rollup let response = client .post("http://127.0.0.1:8080/submit_transaction") .json(&rtx) .send() - .await? - .json::() .await?; - // 3. Handle response - match response { - TransactionResponse::Success { message } => { - println!("Success: {}", message); - }, - TransactionResponse::Error { message } => { - println!("Error: {}", message); - } - } - + println!("Transaction response: {:?}", response.text().await?); + Ok(()) } diff --git a/rollup_core/Cargo.toml b/rollup_core/Cargo.toml index 362545b..71f9446 100644 --- a/rollup_core/Cargo.toml +++ b/rollup_core/Cargo.toml @@ -25,4 +25,5 @@ async-channel = "2.3.1" bincode = "1.3.3" borsh = "0.10" sha1 = "=0.10.0" -digest = "=0.10.7" \ No newline at end of file +digest = "=0.10.7" +sha2 = "0.10" \ No newline at end of file diff --git a/rollup_core/src/delegation.rs b/rollup_core/src/delegation.rs index f8c0c6c..6754421 100644 --- a/rollup_core/src/delegation.rs +++ b/rollup_core/src/delegation.rs @@ -1,3 +1,4 @@ +use sha2::{Sha256, Digest}; use solana_sdk::{ pubkey::Pubkey, instruction::{AccountMeta, Instruction}, @@ -5,6 +6,7 @@ use solana_sdk::{ }; use borsh::{BorshSerialize, BorshDeserialize}; + #[derive(BorshSerialize, BorshDeserialize)] pub struct DelegatedAccount { pub owner: Pubkey, @@ -13,11 +15,6 @@ pub struct DelegatedAccount { pub bump: u8, } -#[derive(BorshSerialize)] -struct AnchorInstruction { - discriminator: [u8; 8], - data: InitializeDelegateArgs, -} #[derive(BorshSerialize)] pub struct InitializeDelegateArgs { @@ -40,13 +37,38 @@ pub fn find_delegation_pda(owner: &Pubkey) -> (Pubkey, u8) { pub fn create_delegation_instruction(owner: &Pubkey, amount: u64) -> Instruction { let (pda, _) = find_delegation_pda(owner); - // Anchor discriminator for "initialize_delegate" - let discriminator = [103, 117, 89, 87, 161, 37, 220, 226]; + // Calculate Anchor discriminator for "initialize_delegate" + let discriminator = { + let mut hasher = Sha256::new(); + hasher.update(b"global:initialize_delegate"); + let result = hasher.finalize(); + let mut disc = [0u8; 8]; + disc.copy_from_slice(&result[..8]); + disc + }; + + let mut ix_data = discriminator.to_vec(); + ix_data.extend(InitializeDelegateArgs { amount }.try_to_vec().unwrap()); + + Instruction { + program_id: get_delegation_program_id(), + accounts: vec![ + AccountMeta::new(*owner, true), + AccountMeta::new(pda, false), + AccountMeta::new_readonly(system_program::id(), false), + ], + data: ix_data, + } +} + +pub fn create_topup_instruction(owner: &Pubkey, amount: u64) -> Instruction { + let (pda, _) = find_delegation_pda(owner); + + // Anchor discriminator for "topup" + let discriminator = [186, 41, 37, 128, 34, 5, 77, 101]; // This should match your Anchor program's "topup" instruction - let ix_data = AnchorInstruction { - discriminator, - data: InitializeDelegateArgs { amount }, - }.try_to_vec().unwrap(); + let mut ix_data = discriminator.to_vec(); + ix_data.extend(InitializeDelegateArgs { amount }.try_to_vec().unwrap()); Instruction { program_id: get_delegation_program_id(), diff --git a/rollup_core/src/delegation_service.rs b/rollup_core/src/delegation_service.rs index 440d00e..9557fbe 100644 --- a/rollup_core/src/delegation_service.rs +++ b/rollup_core/src/delegation_service.rs @@ -1,7 +1,7 @@ use { crate::delegation::{create_delegation_instruction, find_delegation_pda, DelegatedAccount}, anyhow::{anyhow, Result}, borsh::BorshDeserialize, solana_client::rpc_client::RpcClient, solana_sdk::{ - account::{AccountSharedData, ReadableAccount}, message::Message, pubkey::Pubkey, signature::Keypair, transaction::Transaction - }, std::collections::HashMap + account::{AccountSharedData, ReadableAccount}, message::Message, pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction + }, std::collections::HashMap, log }; pub struct DelegationService { @@ -15,31 +15,45 @@ impl DelegationService { Self { rpc_client: RpcClient::new(rpc_url.to_string()), pda_cache: HashMap::new(), - signer: signer, + signer, } } pub fn get_or_fetch_pda(&mut self, user: &Pubkey) -> Result> { let (pda, _) = find_delegation_pda(user); - // Try cache first - if let Some(account) = self.pda_cache.get(&pda) { - if let Ok(delegation) = DelegatedAccount::try_from_slice(&account.data()) { - return Ok(Some((pda, delegation))); - } - } - - // If not in cache, try fetching from chain + // Always try fetching from chain first to be sure match self.rpc_client.get_account(&pda) { Ok(account) => { - if let Ok(delegation) = DelegatedAccount::try_from_slice(&account.data()) { - self.pda_cache.insert(pda, account.into()); - Ok(Some((pda, delegation))) + log::info!( + "Found account for PDA: {}, data length: {}, owner: {}", + pda, + account.data().len(), + account.owner() + ); + // Skip the 8-byte discriminator when deserializing + if account.data().len() > 8 { + if let Ok(delegation) = DelegatedAccount::try_from_slice(&account.data()[8..]) { + self.pda_cache.insert(pda, account.into()); + Ok(Some((pda, delegation))) + } else { + log::warn!( + "Account exists but couldn't deserialize data for PDA: {}. Data: {:?}", + pda, + account.data() + ); + Ok(None) + } } else { + log::warn!("Account data too short for PDA: {}", pda); Ok(None) } } - Err(_) => Ok(None) + Err(e) => { + log::info!("No account found for PDA: {} (Error: {})", pda, e); + self.pda_cache.remove(&pda); + Ok(None) + } } } @@ -47,32 +61,48 @@ impl DelegationService { &mut self, user: &Pubkey, required_amount: u64, - ) -> Result> { // Returns PDA if delegation exists and is sufficient + ) -> Result> { if let Some((pda, delegation)) = self.get_or_fetch_pda(user)? { + log::info!( + "Verifying delegation for {}: current={}, required={}", + user, + delegation.delegated_amount, + required_amount + ); if delegation.delegated_amount >= required_amount { Ok(Some(pda)) } else { Ok(None) } } else { + log::info!("No delegation found for {}", user); Ok(None) } } pub fn create_delegation_transaction( - &self, + &mut self, user: &Pubkey, amount: u64, ) -> Result { + let (pda, _) = find_delegation_pda(user); + + // Use the same method as the initial check + if let Some((_, delegation)) = self.get_or_fetch_pda(user)? { + log::info!( + "Found existing delegation for {} with amount {}. Need to implement top-up.", + user, + delegation.delegated_amount + ); + return Ok(Transaction::default()); + } + let instruction = create_delegation_instruction(user, amount); let recent_blockhash = self.rpc_client.get_latest_blockhash()?; + let message = Message::new(&[instruction], Some(&self.signer.pubkey())); - // Create transaction with recent blockhash - let mut tx = Transaction::new_with_payer( - &[instruction], - Some(user), - ); - tx.message.recent_blockhash = recent_blockhash; // Set the recent blockhash + let mut tx = Transaction::new_unsigned(message); + tx.sign(&[&self.signer], recent_blockhash); Ok(tx) } diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index 1979843..3c5e0eb 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -53,10 +53,10 @@ thread_local! { pub async fn submit_transaction( body: web::Json, - sequencer_sender: web::Data)>>, + sequencer_sender: web::Data>, ) -> actix_web::Result { - // Send both transaction and keypair bytes - sequencer_sender.send((body.sol_transaction.clone(), body.keypair_bytes.clone())) + // Only send the transaction + sequencer_sender.send(body.sol_transaction.clone()) .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; Ok(HttpResponse::Ok().json(TransactionResponse::Success { diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 6879cee..dffee7a 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -4,7 +4,7 @@ use crate::delegation_service::DelegationService; use actix_web::{web, App, HttpResponse, HttpServer}; use async_channel; -use frontend::{FrontendMessage, RollupTransaction}; +use frontend::{FrontendMessage, RollupTransaction, TransactionResponse}; use rollupdb::{RollupDB, RollupDBMessage}; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; @@ -57,6 +57,7 @@ fn main() { // async let asdserver_thread = thread::spawn(|| { let rt = Builder::new_multi_thread() .worker_threads(4) + .enable_time() .build() .unwrap(); @@ -79,6 +80,7 @@ fn main() { // async let rt2 = Builder::new_multi_thread() .worker_threads(4) .enable_io() + .enable_time() .build() .unwrap(); @@ -90,8 +92,14 @@ fn main() { // async .app_data(web::Data::new(frontend_sender.clone())) .app_data(web::Data::new(frontend_receiver.clone())) .route("/", web::get().to(frontend::test)) - .route("/get_transaction", web::post().to(frontend::get_transaction)) - .route("/submit_transaction", web::post().to(frontend::submit_transaction)) + .route( + "/get_transaction", + web::post().to(frontend::get_transaction), + ) + .route( + "/submit_transaction", + web::post().to(frontend::submit_transaction), + ) .route( "/init_delegation_service", { @@ -104,6 +112,7 @@ fn main() { // async }) }, ) + }) .worker_max_blocking_threads(2) .bind("127.0.0.1:8080") diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index a3b2f67..8d84bc8 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -22,7 +22,7 @@ use solana_svm::{ transaction_processing_callback::TransactionProcessingCallback, transaction_processing_result::ProcessedTransaction, transaction_processor::{TransactionBatchProcessor, TransactionProcessingConfig, TransactionProcessingEnvironment} }; use tokio::time::{sleep, Duration}; -use crate::{delegation_service::DelegationService, rollupdb::RollupDBMessage, settle::settle_state}; +use crate::{delegation::find_delegation_pda, delegation_service::DelegationService, rollupdb::RollupDBMessage, settle::settle_state}; use crate::loader::RollupAccountLoader; use crate::processor::*; use crate::errors::RollupErrors; @@ -45,6 +45,63 @@ pub async fn run( // async &rpc_client_temp, ); while let transaction = sequencer_receiver_channel.recv().unwrap() { + let sender = transaction.message.account_keys[0]; + let amount = 1_000_000; + + // Check delegation status first + let needs_delegation = { + let mut delegation_service = delegation_service.write().unwrap(); + match delegation_service.get_or_fetch_pda(&sender)? { + Some((_, delegation)) => { + // Log current and required amounts + log::info!( + "Checking delegation: current amount={}, required amount={}", + delegation.delegated_amount, + amount + ); + delegation.delegated_amount < amount + } + None => { + log::info!("No existing delegation found, creating new one with amount={}", amount); + true + } + } + }; + + if needs_delegation { + // Create delegation transaction outside the lock + let delegation_tx = { + let mut delegation_service = delegation_service.write().unwrap(); + match delegation_service.create_delegation_transaction(&sender, amount) { + Ok(tx) => tx, + Err(e) => { + log::error!("Failed to create delegation transaction: {}", e); + continue; + } + } + }; + + // Submit and confirm delegation + match rpc_client_temp.send_and_confirm_transaction(&delegation_tx) { + Ok(sig) => { + log::info!("Created delegation with signature: {}", sig); + // Wait for confirmation + tokio::time::sleep(Duration::from_secs(2)).await; + + // Update cache after successful delegation + let (pda, _) = find_delegation_pda(&sender); + if let Ok(account) = rpc_client_temp.get_account(&pda) { + delegation_service.write().unwrap() + .update_pda_state(pda, account.into()); + } + } + Err(e) => { + log::error!("Failed to create delegation: {}", e); + continue; // Skip processing this transaction + } + } + } + let accounts_to_lock = transaction.message.account_keys.clone(); for pubkey in accounts_to_lock.iter() { loop { From febed753448782c67e82c38097f74a7c2675ec5f Mon Sep 17 00:00:00 2001 From: timadigwe Date: Thu, 27 Feb 2025 16:11:17 +0100 Subject: [PATCH 38/41] top-up instruction added --- rollup_client/src/main.rs | 65 ++++++++++++++++----------- rollup_core/src/delegation.rs | 51 ++++++++++++++++----- rollup_core/src/delegation_service.rs | 40 ++++++++++++----- rollup_core/src/sequencer.rs | 38 ++++++++++++++-- 4 files changed, 142 insertions(+), 52 deletions(-) diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index febac61..84b37a8 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -43,11 +43,11 @@ async fn main() -> Result<(), Box> { // Connect to devnet let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string()); - // Print balances + // Print initial balances let sender_balance = rpc_client.get_balance(&sender.pubkey()).await?; let receiver_balance = rpc_client.get_balance(&receiver.pubkey()).await?; - println!("Sender {} balance: {} SOL", sender.pubkey(), sender_balance as f64 / 1_000_000_000.0); - println!("Receiver {} balance: {} SOL", receiver.pubkey(), receiver_balance as f64 / 1_000_000_000.0); + println!("Initial Sender {} balance: {} SOL", sender.pubkey(), sender_balance as f64 / 1_000_000_000.0); + println!("Initial Receiver {} balance: {} SOL", receiver.pubkey(), receiver_balance as f64 / 1_000_000_000.0); // Initialize delegation service println!("\nInitializing delegation service..."); @@ -63,33 +63,44 @@ async fn main() -> Result<(), Box> { // Wait for initialization tokio::time::sleep(Duration::from_secs(2)).await; - // Create test transaction - let test_tx = Transaction::new_signed_with_payer( - &[system_instruction::transfer( - &sender.pubkey(), - &receiver.pubkey(), - 250_000_000, // 0.25 SOL - )], - Some(&sender.pubkey()), - &[&sender], - rpc_client.get_latest_blockhash().await?, - ); + // Send 10 transactions + for i in 0..2 { + println!("\nSending transaction {} of 2", i + 1); + + let test_tx = Transaction::new_signed_with_payer( + &[system_instruction::transfer( + &sender.pubkey(), + &receiver.pubkey(), + 250_000_000, // 0.25 SOL + )], + Some(&sender.pubkey()), + &[&sender], + rpc_client.get_latest_blockhash().await?, + ); - // Create RollupTransaction - let rtx = RollupTransaction { - sender: sender.pubkey().to_string(), - sol_transaction: test_tx, - keypair_bytes: sender.to_bytes().to_vec(), - }; + let rtx = RollupTransaction { + sender: sender.pubkey().to_string(), + sol_transaction: test_tx, + keypair_bytes: sender.to_bytes().to_vec(), + }; - // Send to rollup - let response = client - .post("http://127.0.0.1:8080/submit_transaction") - .json(&rtx) - .send() - .await?; + let response = client + .post("http://127.0.0.1:8080/submit_transaction") + .json(&rtx) + .send() + .await?; - println!("Transaction response: {:?}", response.text().await?); + println!("Transaction {} response: {:?}", i + 1, response.text().await?); + + // Small delay between transactions + tokio::time::sleep(Duration::from_secs(1)).await; + } + + // Print final balances + let sender_balance = rpc_client.get_balance(&sender.pubkey()).await?; + let receiver_balance = rpc_client.get_balance(&receiver.pubkey()).await?; + println!("\nFinal Sender {} balance: {} SOL", sender.pubkey(), sender_balance as f64 / 1_000_000_000.0); + println!("Final Receiver {} balance: {} SOL", receiver.pubkey(), receiver_balance as f64 / 1_000_000_000.0); Ok(()) } diff --git a/rollup_core/src/delegation.rs b/rollup_core/src/delegation.rs index 6754421..ed84342 100644 --- a/rollup_core/src/delegation.rs +++ b/rollup_core/src/delegation.rs @@ -37,7 +37,6 @@ pub fn find_delegation_pda(owner: &Pubkey) -> (Pubkey, u8) { pub fn create_delegation_instruction(owner: &Pubkey, amount: u64) -> Instruction { let (pda, _) = find_delegation_pda(owner); - // Calculate Anchor discriminator for "initialize_delegate" let discriminator = { let mut hasher = Sha256::new(); hasher.update(b"global:initialize_delegate"); @@ -53,9 +52,9 @@ pub fn create_delegation_instruction(owner: &Pubkey, amount: u64) -> Instruction Instruction { program_id: get_delegation_program_id(), accounts: vec![ - AccountMeta::new(*owner, true), - AccountMeta::new(pda, false), - AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new(*owner, true), // Signer and fee payer + AccountMeta::new(pda, false), // PDA to be initialized + AccountMeta::new_readonly(system_program::id(), false), // System program for init ], data: ix_data, } @@ -64,18 +63,50 @@ pub fn create_delegation_instruction(owner: &Pubkey, amount: u64) -> Instruction pub fn create_topup_instruction(owner: &Pubkey, amount: u64) -> Instruction { let (pda, _) = find_delegation_pda(owner); - // Anchor discriminator for "topup" - let discriminator = [186, 41, 37, 128, 34, 5, 77, 101]; // This should match your Anchor program's "topup" instruction + // Calculate Anchor discriminator for "top_up" + let discriminator = { + let mut hasher = Sha256::new(); + hasher.update(b"global:top_up"); + let result = hasher.finalize(); + let mut disc = [0u8; 8]; + disc.copy_from_slice(&result[..8]); + disc + }; let mut ix_data = discriminator.to_vec(); - ix_data.extend(InitializeDelegateArgs { amount }.try_to_vec().unwrap()); + ix_data.extend(amount.to_le_bytes()); + + Instruction { + program_id: get_delegation_program_id(), + accounts: vec![ + AccountMeta::new(*owner, true), // Owner must be signer + AccountMeta::new(pda, false), // PDA to be topped up + AccountMeta::new_readonly(system_program::id(), false), // System program + ], + data: ix_data, + } +} + +pub fn create_withdrawal_instruction(pda: &Pubkey, owner: &Pubkey, amount: u64) -> Instruction { + // Calculate Anchor discriminator for "withdraw" + let discriminator = { + let mut hasher = Sha256::new(); + hasher.update(b"global:withdraw"); + let result = hasher.finalize(); + let mut disc = [0u8; 8]; + disc.copy_from_slice(&result[..8]); + disc + }; + + let mut ix_data = discriminator.to_vec(); + ix_data.extend(amount.to_le_bytes()); Instruction { program_id: get_delegation_program_id(), accounts: vec![ - AccountMeta::new(*owner, true), - AccountMeta::new(pda, false), - AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new(*owner, true), // Owner must be a signer + AccountMeta::new(*pda, false), // PDA account + AccountMeta::new_readonly(system_program::id(), false), // System program ], data: ix_data, } diff --git a/rollup_core/src/delegation_service.rs b/rollup_core/src/delegation_service.rs index 9557fbe..76f59f2 100644 --- a/rollup_core/src/delegation_service.rs +++ b/rollup_core/src/delegation_service.rs @@ -1,7 +1,7 @@ use { - crate::delegation::{create_delegation_instruction, find_delegation_pda, DelegatedAccount}, anyhow::{anyhow, Result}, borsh::BorshDeserialize, solana_client::rpc_client::RpcClient, solana_sdk::{ + crate::delegation::{create_delegation_instruction, create_withdrawal_instruction, find_delegation_pda, DelegatedAccount, create_topup_instruction}, anyhow::{anyhow, Result}, borsh::BorshDeserialize, log, solana_client::rpc_client::RpcClient, solana_sdk::{ account::{AccountSharedData, ReadableAccount}, message::Message, pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction - }, std::collections::HashMap, log + }, std::collections::HashMap }; pub struct DelegationService { @@ -85,24 +85,27 @@ impl DelegationService { user: &Pubkey, amount: u64, ) -> Result { - let (pda, _) = find_delegation_pda(user); - - // Use the same method as the initial check - if let Some((_, delegation)) = self.get_or_fetch_pda(user)? { + let instruction = if let Some((_, delegation)) = self.get_or_fetch_pda(user)? { log::info!( - "Found existing delegation for {} with amount {}. Need to implement top-up.", + "Found existing delegation for {} with amount {}. Creating top-up instruction.", user, delegation.delegated_amount ); - return Ok(Transaction::default()); - } + create_topup_instruction(user, amount) + } else { + create_delegation_instruction(user, amount) + }; - let instruction = create_delegation_instruction(user, amount); let recent_blockhash = self.rpc_client.get_latest_blockhash()?; - let message = Message::new(&[instruction], Some(&self.signer.pubkey())); + + let message = Message::new_with_blockhash( + &[instruction], + Some(&self.signer.pubkey()), + &recent_blockhash + ); let mut tx = Transaction::new_unsigned(message); - tx.sign(&[&self.signer], recent_blockhash); + tx.try_sign(&[&self.signer], recent_blockhash)?; Ok(tx) } @@ -110,4 +113,17 @@ impl DelegationService { pub fn update_pda_state(&mut self, pda: Pubkey, account: AccountSharedData) { self.pda_cache.insert(pda, account); } + + + pub fn create_withdrawal_transaction(&mut self, pda: &Pubkey, owner: &Pubkey, amount: u64) -> Result { + let instruction = create_withdrawal_instruction(pda, owner, amount); + + let recent_blockhash = self.rpc_client.get_latest_blockhash()?; + let message = Message::new(&[instruction], Some(&self.signer.pubkey())); + + let mut tx = Transaction::new_unsigned(message); + tx.sign(&[&self.signer], recent_blockhash); + + Ok(tx) + } } diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 8d84bc8..59ede4f 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -26,6 +26,7 @@ use crate::{delegation::find_delegation_pda, delegation_service::DelegationServi use crate::loader::RollupAccountLoader; use crate::processor::*; use crate::errors::RollupErrors; +use solana_client::nonblocking::rpc_client::RpcClient as NonblockingRpcClient; pub async fn run( // async sequencer_receiver_channel: CBReceiver, // CBReceiver @@ -44,7 +45,7 @@ pub async fn run( // async let mut rollup_account_loader = RollupAccountLoader::new( &rpc_client_temp, ); - while let transaction = sequencer_receiver_channel.recv().unwrap() { + while let Ok(transaction) = sequencer_receiver_channel.recv() { let sender = transaction.message.account_keys[0]; let amount = 1_000_000; @@ -250,9 +251,40 @@ pub async fn run( // async // Call settle if transaction amount since last settle hits 10 - if tx_counter >= 10 { + if tx_counter >= 2 { log::info!("Start bundling!"); - //bundle transfer tx test + + // Get the current user's delegation and withdraw funds + let (pda, delegation) = { + let mut delegation_service = delegation_service.write().unwrap(); + if let Some(delegation_info) = delegation_service.get_or_fetch_pda(&sender)? { + delegation_info + } else { + log::error!("No delegation found for {} during withdrawal", sender); + return Ok(()); + } + }; + + // Create and send withdrawal transaction + let withdrawal_tx = { + let mut delegation_service = delegation_service.write().unwrap(); + delegation_service.create_withdrawal_transaction(&pda, &sender, delegation.delegated_amount)? + }; + + // Submit withdrawal transaction synchronously + match rpc_client_temp.send_and_confirm_transaction(&withdrawal_tx) { + Ok(sig) => { + log::info!("Withdrew {} lamports from delegation {}, signature: {}", + delegation.delegated_amount, pda, sig); + }, + Err(e) => { + log::error!("Failed to withdraw from delegation {}: {}", pda, e); + } + } + + // Reset counter after processing + tx_counter = 0; + rollupdb_sender.send(RollupDBMessage { lock_accounts: None, add_processed_transaction: None, From 4be701d7d88926f20f95d8997d2ee2ab3e396ed5 Mon Sep 17 00:00:00 2001 From: timadigwe Date: Fri, 28 Feb 2025 13:23:13 +0100 Subject: [PATCH 39/41] merged testing-x-williams into delegation_service --- rollup_client/src/main.rs | 68 ++++++++++------ rollup_core/src/bundler.rs | 112 ++++++++++++++++++++++++++ rollup_core/src/delegation.rs | 6 +- rollup_core/src/delegation_service.rs | 64 ++++++--------- rollup_core/src/frontend.rs | 19 ++--- rollup_core/src/main.rs | 1 + rollup_core/src/rollupdb.rs | 20 +++++ rollup_core/src/sequencer.rs | 22 ++++- 8 files changed, 232 insertions(+), 80 deletions(-) create mode 100644 rollup_core/src/bundler.rs diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index 84b37a8..a96c70e 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -19,7 +19,6 @@ use std::{collections::HashMap, str::FromStr, time::Duration, fs}; struct RollupTransaction { sender: String, sol_transaction: Transaction, - keypair_bytes: Vec, } #[derive(Serialize, Deserialize, Debug)] @@ -49,39 +48,64 @@ async fn main() -> Result<(), Box> { println!("Initial Sender {} balance: {} SOL", sender.pubkey(), sender_balance as f64 / 1_000_000_000.0); println!("Initial Receiver {} balance: {} SOL", receiver.pubkey(), receiver_balance as f64 / 1_000_000_000.0); - // Initialize delegation service - println!("\nInitializing delegation service..."); + // Initialize delegation service for sender + println!("\nInitializing delegation service for sender..."); let client = reqwest::Client::new(); let response = client .post("http://127.0.0.1:8080/init_delegation_service") .body(sender.to_bytes().to_vec()) .send() .await?; - - println!("Delegation service init response: {:?}", response.text().await?); - + println!("Sender delegation service init response: {:?}", response.text().await?); + // Wait for initialization tokio::time::sleep(Duration::from_secs(2)).await; - // Send 10 transactions - for i in 0..2 { - println!("\nSending transaction {} of 2", i + 1); - - let test_tx = Transaction::new_signed_with_payer( - &[system_instruction::transfer( - &sender.pubkey(), - &receiver.pubkey(), - 250_000_000, // 0.25 SOL - )], - Some(&sender.pubkey()), - &[&sender], + // Initialize delegation service for receiver + println!("\nInitializing delegation service for receiver..."); + let response = client + .post("http://127.0.0.1:8080/init_delegation_service") + .body(receiver.to_bytes().to_vec()) + .send() + .await?; + println!("Receiver delegation service init response: {:?}", response.text().await?); + + // Wait for initialization + tokio::time::sleep(Duration::from_secs(2)).await; + + // Create test transactions + let amounts = vec![5, -3,]; + let mut txs = Vec::new(); + + for amount in amounts { + let (from, to, lamports) = if amount > 0 { + (&sender, &receiver, amount as u64) + } else { + (&receiver, &sender, (-amount) as u64) + }; + + let ix = system_instruction::transfer( + &from.pubkey(), + &to.pubkey(), + lamports * (LAMPORTS_PER_SOL / 10) + ); + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&from.pubkey()), + &[from], rpc_client.get_latest_blockhash().await?, ); + txs.push(tx); + } + + // Submit transactions + println!("\nSubmitting transactions..."); + for (i, tx) in txs.into_iter().enumerate() { let rtx = RollupTransaction { sender: sender.pubkey().to_string(), - sol_transaction: test_tx, - keypair_bytes: sender.to_bytes().to_vec(), + sol_transaction: tx, }; let response = client @@ -89,10 +113,8 @@ async fn main() -> Result<(), Box> { .json(&rtx) .send() .await?; - + println!("Transaction {} response: {:?}", i + 1, response.text().await?); - - // Small delay between transactions tokio::time::sleep(Duration::from_secs(1)).await; } diff --git a/rollup_core/src/bundler.rs b/rollup_core/src/bundler.rs new file mode 100644 index 0000000..e46239a --- /dev/null +++ b/rollup_core/src/bundler.rs @@ -0,0 +1,112 @@ +use std::collections::HashMap; + +use solana_sdk::{instruction::{CompiledInstruction, Instruction}, pubkey::Pubkey, system_instruction::{self, SystemInstruction}, system_program, transaction::Transaction}; +use bincode::deserialize; + +pub fn get_transaction_instructions(tx: &Transaction) -> Vec{ + tx.message.instructions.clone() +} + +pub fn is_transfer_ix(cix: &CompiledInstruction, account_keys: &[Pubkey]) -> bool { + if cix.program_id_index as usize >= account_keys.len(){ + return false; + } + let program_id = account_keys[cix.program_id_index as usize]; + if program_id != system_program::ID{ + return false + } + + matches!( + deserialize::(&cix.data), + Ok(SystemInstruction::Transfer { .. }) + ) +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +struct TBundlerKey { + keys: [Pubkey; 2], +} + +pub struct TransferBundler { + transfers: HashMap, +} + +impl TransferBundler { + pub fn new() -> Self { + Self { + transfers: HashMap::new() + } + } + + pub fn parse_compiled_instruction(ix: &CompiledInstruction, account_keys: &[Pubkey]) -> Option<(Pubkey, Pubkey, i128)>{ + //Ensure the instruction is from System Program (where transfer is from) + if ix.program_id_index as usize >= account_keys.len() || account_keys[ix.program_id_index as usize] != system_program::ID{ + return None; + } + //Ensure we have at least 2 accounts for transfer and enough data for SOL amount + if ix.accounts.len() < 2 || ix.data.len() < 8 { + return None; + } + + //Get accounts involved in transfer and amount to be transferred + let from = account_keys[ix.accounts[0] as usize]; + let to = account_keys[ix.accounts[1] as usize]; + + log::info!("FROM: {:?}", from.to_string()); + log::info!("TO: {:?}", to.to_string()); + log::info!("IX DATA: {:?}", ix.data); + + let amount = u64::from_le_bytes(ix.data[4..12].try_into().ok()?); + Some((from, to, amount as i128)) + } + + pub fn parse_instruction(ix: &Instruction) -> Option<(Pubkey, Pubkey, i128)>{ + //Enusre ix is owned by system program + if ix.program_id != system_program::ID{ + return None; + } + + //Ensure we have enough accounts + if ix.accounts.len() < 2{ + return None; + } + let from = ix.accounts[0].pubkey; + let to = ix.accounts[1].pubkey; + let amount = u64::from_le_bytes(ix.data[4..].try_into().ok()?); + + log::info!("FROM: {:?}", from.to_string()); + log::info!("TO: {:?}", to.to_string()); + log::info!("AMOUNT: {amount}"); + log::info!("IX DATA: {:?}", ix.data); + + + Some((from, to, amount as i128)) + } + + //Parses transactions and add transfer ixs to TransferBundler + pub fn bundle(&mut self, transaction: Transaction){ + let ixs = get_transaction_instructions(&transaction); + let account_keys: &[Pubkey] = &transaction.message.account_keys; + for ix in ixs { + if is_transfer_ix(&ix, account_keys){ + let (from, to, amount) = Self::parse_compiled_instruction(&ix, account_keys).unwrap(); + let mut keys = [from, to]; + keys.sort(); + + *self.transfers.entry(TBundlerKey {keys}).or_default() += if from == keys[0] {amount} else {-amount}; + } + } + } + + pub fn generate_final(self) -> Vec { + self.transfers.into_iter().filter_map(|(map_key, val)| { + if val < 0 { + Some(system_instruction::transfer(&map_key.keys[1], &map_key.keys[0], val.unsigned_abs() as u64)) + } else if val > 0 { + Some(system_instruction::transfer(&map_key.keys[0], &map_key.keys[1], val as u64)) + } else { + None + } + }).collect() + } +} \ No newline at end of file diff --git a/rollup_core/src/delegation.rs b/rollup_core/src/delegation.rs index ed84342..46771f6 100644 --- a/rollup_core/src/delegation.rs +++ b/rollup_core/src/delegation.rs @@ -52,9 +52,9 @@ pub fn create_delegation_instruction(owner: &Pubkey, amount: u64) -> Instruction Instruction { program_id: get_delegation_program_id(), accounts: vec![ - AccountMeta::new(*owner, true), // Signer and fee payer - AccountMeta::new(pda, false), // PDA to be initialized - AccountMeta::new_readonly(system_program::id(), false), // System program for init + AccountMeta::new(*owner, true), // Owner must be signer + AccountMeta::new(pda, false), // PDA account + AccountMeta::new_readonly(system_program::id(), false), // System program ], data: ix_data, } diff --git a/rollup_core/src/delegation_service.rs b/rollup_core/src/delegation_service.rs index 76f59f2..f1310a0 100644 --- a/rollup_core/src/delegation_service.rs +++ b/rollup_core/src/delegation_service.rs @@ -7,18 +7,25 @@ use { pub struct DelegationService { rpc_client: RpcClient, pda_cache: HashMap, - signer: Keypair, + signers: HashMap, // Store multiple signers } impl DelegationService { - pub fn new(rpc_url: &str, signer: Keypair) -> Self { + pub fn new(rpc_url: &str, initial_signer: Keypair) -> Self { + let mut signers = HashMap::new(); + signers.insert(initial_signer.pubkey(), initial_signer); + Self { rpc_client: RpcClient::new(rpc_url.to_string()), pda_cache: HashMap::new(), - signer, + signers, } } + pub fn add_signer(&mut self, signer: Keypair) { + self.signers.insert(signer.pubkey(), signer); + } + pub fn get_or_fetch_pda(&mut self, user: &Pubkey) -> Result> { let (pda, _) = find_delegation_pda(user); @@ -57,55 +64,33 @@ impl DelegationService { } } - pub fn verify_delegation_for_transaction( - &mut self, - user: &Pubkey, - required_amount: u64, - ) -> Result> { - if let Some((pda, delegation)) = self.get_or_fetch_pda(user)? { - log::info!( - "Verifying delegation for {}: current={}, required={}", - user, - delegation.delegated_amount, - required_amount - ); - if delegation.delegated_amount >= required_amount { - Ok(Some(pda)) - } else { - Ok(None) - } - } else { - log::info!("No delegation found for {}", user); - Ok(None) - } - } - pub fn create_delegation_transaction( &mut self, user: &Pubkey, amount: u64, ) -> Result { - let instruction = if let Some((_, delegation)) = self.get_or_fetch_pda(user)? { - log::info!( - "Found existing delegation for {} with amount {}. Creating top-up instruction.", - user, - delegation.delegated_amount - ); + // First check PDA existence + let has_existing = self.get_or_fetch_pda(user)?.is_some(); + + // Then get signer after PDA check + let signer = self.signers.get(user) + .ok_or_else(|| anyhow!("No delegation signer found for {}", user))?; + + let instruction = if has_existing { create_topup_instruction(user, amount) } else { create_delegation_instruction(user, amount) }; let recent_blockhash = self.rpc_client.get_latest_blockhash()?; - let message = Message::new_with_blockhash( &[instruction], - Some(&self.signer.pubkey()), + Some(&signer.pubkey()), &recent_blockhash ); let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&[&self.signer], recent_blockhash)?; + tx.try_sign(&[signer], recent_blockhash)?; Ok(tx) } @@ -114,15 +99,18 @@ impl DelegationService { self.pda_cache.insert(pda, account); } - pub fn create_withdrawal_transaction(&mut self, pda: &Pubkey, owner: &Pubkey, amount: u64) -> Result { let instruction = create_withdrawal_instruction(pda, owner, amount); + // Get the signer for the owner + let signer = self.signers.get(owner) + .ok_or_else(|| anyhow!("No delegation signer found for {}", owner))?; + let recent_blockhash = self.rpc_client.get_latest_blockhash()?; - let message = Message::new(&[instruction], Some(&self.signer.pubkey())); + let message = Message::new(&[instruction], Some(&signer.pubkey())); let mut tx = Transaction::new_unsigned(message); - tx.sign(&[&self.signer], recent_blockhash); + tx.sign(&[signer], recent_blockhash); Ok(tx) } diff --git a/rollup_core/src/frontend.rs b/rollup_core/src/frontend.rs index 3c5e0eb..9e3649c 100644 --- a/rollup_core/src/frontend.rs +++ b/rollup_core/src/frontend.rs @@ -10,12 +10,6 @@ use crossbeam::channel::{Sender as CBSender, Receiver as CBReceiver}; use serde::{Deserialize, Serialize}; use solana_sdk::hash::Hash; // keccak::Hash use solana_sdk::transaction::Transaction; -use { - crate::delegation_service::DelegationService, - solana_sdk::pubkey::Pubkey, - std::sync::{RwLock}, -}; - use crate::rollupdb::RollupDBMessage; // message format to send found transaction from db to frontend @@ -36,7 +30,6 @@ pub struct GetTransaction { pub struct RollupTransaction { pub sender: String, pub sol_transaction: Transaction, - pub keypair_bytes: Vec, } #[derive(Serialize, Deserialize, Debug)] @@ -46,16 +39,16 @@ pub enum TransactionResponse { Error { message: String }, } -// Add thread local storage for keypair -thread_local! { - static KEYPAIR_STORAGE: RefCell>> = RefCell::new(None); -} pub async fn submit_transaction( body: web::Json, sequencer_sender: web::Data>, ) -> actix_web::Result { - // Only send the transaction + // Validate transaction structure with serialization in function signature + log::info!("Submitted transaction"); + log::info!("{body:?}"); + + // Send transaction to sequencer sequencer_sender.send(body.sol_transaction.clone()) .map_err(|e| actix_web::error::ErrorInternalServerError(e.to_string()))?; @@ -82,6 +75,7 @@ pub async fn get_transaction( frontend_get_tx: Some(Hash::new(body.get_tx.as_bytes())), add_settle_proof: None, get_account: None, + bundle_tx: false }) .await .unwrap(); @@ -90,7 +84,6 @@ pub async fn get_transaction( return Ok(HttpResponse::Ok().json(RollupTransaction { sender: "Rollup RPC".into(), sol_transaction: frontend_message.transaction.unwrap(), - keypair_bytes: Vec::new(), })); // Ok(HttpResponse::Ok().json(HashMap::from([("Transaction status", "requested")]))) } diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index dffee7a..d2b82d1 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -18,6 +18,7 @@ mod sequencer; mod settle; mod processor; mod loader; +mod bundler; mod errors; mod delegation; mod delegation_service; diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index efca84f..901d9bc 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -12,6 +12,7 @@ use std::{ }; use tokio::sync::oneshot; use crate::frontend::FrontendMessage; +use crate::bundler::*; #[derive(Serialize, Deserialize)] pub struct RollupDBMessage { @@ -22,6 +23,8 @@ pub struct RollupDBMessage { pub add_settle_proof: Option, pub get_account: Option, // pub response: Option, + //Testing purposes + pub bundle_tx: bool } #[derive(Serialize, Debug, Default)] @@ -47,6 +50,7 @@ impl RollupDB { pda_mappings: HashMap::new(), }; while let Ok(message) = rollup_db_receiver.recv() { + log::info!("Received RollupDBMessage"); if let Some(accounts_to_lock) = message.lock_accounts { let mut information_to_send: Vec<(Pubkey, AccountSharedData)> = Vec::new(); log::info!("locking: {:?}", db.accounts_db); @@ -84,6 +88,7 @@ impl RollupDB { account_sender.send(Some(information_to_send)).await.unwrap(); // log::info!("2: {:#?}", db.locked_accounts); } else if let Some(get_this_hash_tx) = message.frontend_get_tx { + log::info!("Getting tx for frontend"); let req_tx = db.transactions.get(&get_this_hash_tx).unwrap(); frontend_sender @@ -118,6 +123,21 @@ impl RollupDB { // communication channel with database // communcation with the frontend } + else if message.bundle_tx { + log::info!("BUNDLING TX"); + let mut tx_bundler = TransferBundler::new(); + for (_, tx) in db.transactions.clone() { + tx_bundler.bundle(tx); + } + let final_ixs = tx_bundler.generate_final(); + log::info!("\nFinal Transfer Ixs:"); + for ix in final_ixs{ + if let Some((from, to, amount)) = TransferBundler::parse_instruction(&ix){ + } + } + log::info!("BUNDLING DONE"); + db.transactions.clear(); + } else if let Some(pubkey) = message.get_account { if db.locked_accounts.contains_key(&pubkey) { sender_locked_accounts.send(true).await.unwrap(); diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index 59ede4f..facceb2 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -25,8 +25,9 @@ use tokio::time::{sleep, Duration}; use crate::{delegation::find_delegation_pda, delegation_service::DelegationService, rollupdb::RollupDBMessage, settle::settle_state}; use crate::loader::RollupAccountLoader; use crate::processor::*; +use crate::bundler::*; use crate::errors::RollupErrors; -use solana_client::nonblocking::rpc_client::RpcClient as NonblockingRpcClient; + pub async fn run( // async sequencer_receiver_channel: CBReceiver, // CBReceiver @@ -34,9 +35,7 @@ pub async fn run( // async account_reciever: Receiver>>, receiver_locked_accounts: Receiver, delegation_service: Arc>, - // rx: tokio::sync::oneshot::Receiver> // sync_ver_sender ) -> Result<()> { - // let (tx, rx) = oneshot::channel(); // Create a channel to wait for response let mut tx_counter = 0u32; @@ -114,6 +113,7 @@ pub async fn run( // async add_new_data: None, add_processed_transaction: None, get_account: Some(*pubkey), + bundle_tx: false }) .map_err(|_| anyhow!("failed to send message to rollupdb"))?; @@ -134,6 +134,7 @@ pub async fn run( // async add_processed_transaction: None, get_account: None, // response: Some(true), + bundle_tx: false }) .map_err(|_| anyhow!("failed to send message to rollupdb"))?; @@ -244,10 +245,24 @@ pub async fn run( // async frontend_get_tx: None, add_settle_proof: None, get_account: None, + bundle_tx: false }) .unwrap(); + //View sent processed tx details + let ixs = get_transaction_instructions(&transaction); + let acc_keys: &[Pubkey] = &transaction.message.account_keys; + if let Some((from, to, amount)) = TransferBundler::parse_compiled_instruction(&ixs[0], acc_keys) { + log::info!(" + Transaction Info\n + From: {from:?}\n + To: {to:?}\n + Amount: {amount} + + ") + } + // Call settle if transaction amount since last settle hits 10 @@ -292,6 +307,7 @@ pub async fn run( // async get_account: None, add_new_data: None, frontend_get_tx: None, + bundle_tx: true }).unwrap(); } } From 8fce896d68a8ee1aea4999af5e167481abd733ae Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Mon, 3 Mar 2025 19:39:58 +0100 Subject: [PATCH 40/41] add settler --- rollup_client/src/main.rs | 1 + rollup_core/src/delegation_service.rs | 7 +++++-- rollup_core/src/main.rs | 9 +++++++-- rollup_core/src/rollupdb.rs | 14 ++++++++++++-- rollup_core/src/sequencer.rs | 23 +++++++++++++---------- rollup_core/src/settle.rs | 27 +++++++++++++++++++++++---- 6 files changed, 61 insertions(+), 20 deletions(-) diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index a96c70e..dd88abf 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -38,6 +38,7 @@ async fn main() -> Result<(), Box> { // Load existing keypairs from files let sender = Keypair::from_bytes(&fs::read("keys/sender.json")?)?; let receiver = Keypair::from_bytes(&fs::read("keys/receiver.json")?)?; + println!("rec: {:?}", receiver.pubkey()); // Connect to devnet let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string()); diff --git a/rollup_core/src/delegation_service.rs b/rollup_core/src/delegation_service.rs index f1310a0..6c05a91 100644 --- a/rollup_core/src/delegation_service.rs +++ b/rollup_core/src/delegation_service.rs @@ -71,7 +71,7 @@ impl DelegationService { ) -> Result { // First check PDA existence let has_existing = self.get_or_fetch_pda(user)?.is_some(); - + log::info!("create::: signers are here: {:?}", self.signers); // Then get signer after PDA check let signer = self.signers.get(user) .ok_or_else(|| anyhow!("No delegation signer found for {}", user))?; @@ -101,7 +101,7 @@ impl DelegationService { pub fn create_withdrawal_transaction(&mut self, pda: &Pubkey, owner: &Pubkey, amount: u64) -> Result { let instruction = create_withdrawal_instruction(pda, owner, amount); - + log::info!("signers are here: {:?}", self.signers); // Get the signer for the owner let signer = self.signers.get(owner) .ok_or_else(|| anyhow!("No delegation signer found for {}", owner))?; @@ -114,4 +114,7 @@ impl DelegationService { Ok(tx) } + pub fn get_keypair(&self, user: &Pubkey) -> Option<&Keypair> { + self.signers.get(user) + } } diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index d2b82d1..31542db 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -53,6 +53,7 @@ fn main() { // async )); let delegation_service_clone = delegation_service.clone(); + let delegation_service_clone_1 = delegation_service.clone(); let asdserver_thread = thread::spawn(|| { @@ -71,12 +72,14 @@ fn main() { // async delegation_service_clone, ).await.unwrap() }); - rt.block_on(RollupDB::run(rollupdb_receiver, fe_2, account_sender, sender_locked_account)); + + + rt.block_on(RollupDB::run(rollupdb_receiver, fe_2, account_sender, sender_locked_account, delegation_service_clone_1)); }); // Spawn the Actix Web server in a separate thread - let server_thread = thread::spawn(|| { + let server_thread = thread::spawn( || { // Create a separate Tokio runtime for Actix Web let rt2 = Builder::new_multi_thread() .worker_threads(4) @@ -109,6 +112,8 @@ fn main() { // async let keypair = Keypair::from_bytes(&body).unwrap(); *delegation_service.write().unwrap() = DelegationService::new("https://api.devnet.solana.com", keypair); + log::info!("Delegation service initialized___"); + // log::info!("{:?}", ) HttpResponse::Ok() }) }, diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index 901d9bc..a237c31 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -8,10 +8,10 @@ use solana_sdk::{ use crossbeam::channel::{Receiver as CBReceiver, Sender as CBSender}; use std::{ collections::{HashMap, HashSet}, - default, + default, sync::{Arc, RwLock}, }; use tokio::sync::oneshot; -use crate::frontend::FrontendMessage; +use crate::{delegation_service::DelegationService, frontend::FrontendMessage, settle::settle_state}; use crate::bundler::*; #[derive(Serialize, Deserialize)] @@ -42,6 +42,7 @@ impl RollupDB { frontend_sender: Sender, account_sender: Sender>>, sender_locked_accounts: Sender, + delegation_service: Arc>, ) { let mut db = RollupDB { accounts_db: HashMap::new(), @@ -130,6 +131,13 @@ impl RollupDB { tx_bundler.bundle(tx); } let final_ixs = tx_bundler.generate_final(); + + // let pubkey_user = &final_ixs[0].accounts[0].pubkey; + // let del_service = delegation_service.read().unwrap(); + // let user_keypair = del_service.get_keypair(pubkey_user).unwrap(); + + // settle_state(&final_ixs , user_keypair).await.unwrap(); + log::info!("\nFinal Transfer Ixs:"); for ix in final_ixs{ if let Some((from, to, amount)) = TransferBundler::parse_instruction(&ix){ @@ -137,6 +145,8 @@ impl RollupDB { } log::info!("BUNDLING DONE"); db.transactions.clear(); + + } else if let Some(pubkey) = message.get_account { if db.locked_accounts.contains_key(&pubkey) { diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index facceb2..f5c4e2f 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -46,7 +46,7 @@ pub async fn run( // async ); while let Ok(transaction) = sequencer_receiver_channel.recv() { let sender = transaction.message.account_keys[0]; - let amount = 1_000_000; + let amount = 1_000_000_000; // Check delegation status first let needs_delegation = { @@ -267,6 +267,18 @@ pub async fn run( // async // Call settle if transaction amount since last settle hits 10 if tx_counter >= 2 { + rollupdb_sender.send(RollupDBMessage { + lock_accounts: None, + add_processed_transaction: None, + add_settle_proof: None, + get_account: None, + add_new_data: None, + frontend_get_tx: None, + bundle_tx: true + }).unwrap(); + + + log::info!("Start bundling!"); // Get the current user's delegation and withdraw funds @@ -300,15 +312,6 @@ pub async fn run( // async // Reset counter after processing tx_counter = 0; - rollupdb_sender.send(RollupDBMessage { - lock_accounts: None, - add_processed_transaction: None, - add_settle_proof: None, - get_account: None, - add_new_data: None, - frontend_get_tx: None, - bundle_tx: true - }).unwrap(); } } Ok(()) diff --git a/rollup_core/src/settle.rs b/rollup_core/src/settle.rs index e16d7d8..102279f 100644 --- a/rollup_core/src/settle.rs +++ b/rollup_core/src/settle.rs @@ -1,17 +1,36 @@ use anyhow::Result; +use async_channel::Receiver; use solana_client::nonblocking::rpc_client::RpcClient; -use solana_sdk::{hash::Hash, transaction::Transaction}; +use solana_sdk::{hash::Hash, instruction::Instruction, message::Message, signature::Keypair, transaction::Transaction}; // Settle the state on solana, called by sequencer -pub async fn settle_state(proof: Hash) -> Result { +pub async fn settle_state(vec_of_instructions: &Vec, signer: &Keypair) -> Result { + let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); + let ix = vec_of_instructions[0].clone(); + let payer = &ix.accounts[0].pubkey; + + let tx = Transaction::new_signed_with_payer( + vec_of_instructions.as_slice(), + Some(payer), + &[signer], + rpc_client.get_latest_blockhash().await? + ); + + + + // let tx = Transaction::new(from_keypairs, message, recent_blockhash) + + // let message = Message::new_with_compiled_instructions(num_required_signatures, num_readonly_signed_accounts, num_readonly_unsigned_accounts, account_keys, recent_blockhash, instructions) + // Create proof transaction, calling the right function in the contract - let transaction = Transaction::default(); // Send transaction to contract on chain let settle_tx_hash = rpc_client - .send_and_confirm_transaction(&transaction) + .send_and_confirm_transaction(&tx) .await?; + + log::info!("Settled state: {}", settle_tx_hash.to_string()); Ok(settle_tx_hash.to_string()) } From 8c4c8d4094dd833c0677f73e262148cca68b70f6 Mon Sep 17 00:00:00 2001 From: NickNut <68846529+Se76@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:39:52 +0100 Subject: [PATCH 41/41] add final changes to the rollup --- rollup_client/src/main.rs | 4 ++-- rollup_core/src/main.rs | 12 ++++++++++++ rollup_core/src/rollupdb.rs | 8 ++++---- rollup_core/src/sequencer.rs | 8 ++++---- rollup_core/src/settle.rs | 20 ++++++++++++++------ 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/rollup_client/src/main.rs b/rollup_client/src/main.rs index dd88abf..78ad9bb 100644 --- a/rollup_client/src/main.rs +++ b/rollup_client/src/main.rs @@ -65,7 +65,7 @@ async fn main() -> Result<(), Box> { // Initialize delegation service for receiver println!("\nInitializing delegation service for receiver..."); let response = client - .post("http://127.0.0.1:8080/init_delegation_service") + .post("http://127.0.0.1:8080/add_delegation_signer") .body(receiver.to_bytes().to_vec()) .send() .await?; @@ -75,7 +75,7 @@ async fn main() -> Result<(), Box> { tokio::time::sleep(Duration::from_secs(2)).await; // Create test transactions - let amounts = vec![5, -3,]; + let amounts = vec![5, -3, 9, -10, 1, -10, 4, -3, 9, -6]; let mut txs = Vec::new(); for amount in amounts { diff --git a/rollup_core/src/main.rs b/rollup_core/src/main.rs index 31542db..50765d5 100644 --- a/rollup_core/src/main.rs +++ b/rollup_core/src/main.rs @@ -118,6 +118,18 @@ fn main() { // async }) }, ) + .route( + "/add_delegation_signer", + { + let delegation_service = delegation_service.clone(); + web::post().to(move |body: web::Bytes| { + let keypair = Keypair::from_bytes(&body).unwrap(); + delegation_service.write().unwrap().add_signer(keypair); + log::info!("Added signer to delegation service"); + HttpResponse::Ok() + }) + }, + ) }) .worker_max_blocking_threads(2) diff --git a/rollup_core/src/rollupdb.rs b/rollup_core/src/rollupdb.rs index a237c31..e17b61e 100644 --- a/rollup_core/src/rollupdb.rs +++ b/rollup_core/src/rollupdb.rs @@ -132,11 +132,11 @@ impl RollupDB { } let final_ixs = tx_bundler.generate_final(); - // let pubkey_user = &final_ixs[0].accounts[0].pubkey; - // let del_service = delegation_service.read().unwrap(); - // let user_keypair = del_service.get_keypair(pubkey_user).unwrap(); + let pubkey_user = &final_ixs[0].accounts[0].pubkey; + let del_service = delegation_service.read().unwrap(); + let user_keypair = del_service.get_keypair(pubkey_user).unwrap(); - // settle_state(&final_ixs , user_keypair).await.unwrap(); + settle_state(&final_ixs , user_keypair).await.unwrap(); log::info!("\nFinal Transfer Ixs:"); for ix in final_ixs{ diff --git a/rollup_core/src/sequencer.rs b/rollup_core/src/sequencer.rs index f5c4e2f..50ec00d 100644 --- a/rollup_core/src/sequencer.rs +++ b/rollup_core/src/sequencer.rs @@ -48,8 +48,8 @@ pub async fn run( // async let sender = transaction.message.account_keys[0]; let amount = 1_000_000_000; - // Check delegation status first - let needs_delegation = { + // Check delegation status first + let needs_delegation = { let mut delegation_service = delegation_service.write().unwrap(); match delegation_service.get_or_fetch_pda(&sender)? { Some((_, delegation)) => { @@ -266,7 +266,7 @@ pub async fn run( // async // Call settle if transaction amount since last settle hits 10 - if tx_counter >= 2 { + if tx_counter >= 10 { rollupdb_sender.send(RollupDBMessage { lock_accounts: None, add_processed_transaction: None, @@ -277,7 +277,7 @@ pub async fn run( // async bundle_tx: true }).unwrap(); - + log::info!("Start bundling!"); diff --git a/rollup_core/src/settle.rs b/rollup_core/src/settle.rs index 102279f..556e1a1 100644 --- a/rollup_core/src/settle.rs +++ b/rollup_core/src/settle.rs @@ -1,24 +1,31 @@ use anyhow::Result; use async_channel::Receiver; -use solana_client::nonblocking::rpc_client::RpcClient; +use solana_client::rpc_client::RpcClient; use solana_sdk::{hash::Hash, instruction::Instruction, message::Message, signature::Keypair, transaction::Transaction}; // Settle the state on solana, called by sequencer pub async fn settle_state(vec_of_instructions: &Vec, signer: &Keypair) -> Result { - let rpc_client = RpcClient::new("https://api.devnet.solana.com".into()); + println!("Settling state"); + + let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string()); let ix = vec_of_instructions[0].clone(); let payer = &ix.accounts[0].pubkey; + let ixs = vec_of_instructions.as_slice(); + let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); + log::info!("Almost settled stateee: {:?}", recent_blockhash); + + let tx = Transaction::new_signed_with_payer( - vec_of_instructions.as_slice(), + ixs, Some(payer), &[signer], - rpc_client.get_latest_blockhash().await? + recent_blockhash, ); - + log::info!("Almost settled state: {:?}", tx); // let tx = Transaction::new(from_keypairs, message, recent_blockhash) @@ -29,7 +36,8 @@ pub async fn settle_state(vec_of_instructions: &Vec, signer: &Keypa // Send transaction to contract on chain let settle_tx_hash = rpc_client .send_and_confirm_transaction(&tx) - .await?; + // .await?; + .unwrap(); log::info!("Settled state: {}", settle_tx_hash.to_string()); Ok(settle_tx_hash.to_string())