Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
d7b028d
add comments and notes
Se76 Jan 30, 2025
887e03b
add some changes
Se76 Feb 4, 2025
5d7c876
add some changes
Se76 Feb 5, 2025
0aea08b
pew
Se76 Feb 5, 2025
62e4012
add tests and some work
Se76 Feb 7, 2025
b26efdd
change logic from message processor to transaction batch processor
Se76 Feb 8, 2025
dfb17f9
add some testing changes
Se76 Feb 11, 2025
0e805b0
added bundler --untested
NkamaWilliams Feb 12, 2025
fc00f55
small changes for testing purposes
NkamaWilliams Feb 12, 2025
f398e44
tx bundler v0.1
NkamaWilliams Feb 13, 2025
23be728
adding connection to the locking, unlocking, processing... in the rol…
Se76 Feb 16, 2025
919efde
completely connecting all to the database and testing it's work
Se76 Feb 16, 2025
3a27846
add fucntionality of checking locked accounts
Se76 Feb 17, 2025
fce84ed
better testing
NkamaWilliams Feb 18, 2025
54780f2
delegation service in the frontend
Timadigwe Feb 18, 2025
918a5a4
Create README.md
wdotsol Feb 18, 2025
2f8182f
updated keypair path
NkamaWilliams Feb 18, 2025
20ddac7
Merge branch 'williams-test' of https://github.com/Se76/Basic_Rollup_…
NkamaWilliams Feb 18, 2025
64c273d
merged testing | williams
NkamaWilliams Feb 18, 2025
5b415a1
fixed bugs in rollup
NkamaWilliams Feb 18, 2025
a75dfe3
Update README.md
wdotsol Feb 18, 2025
ee5e84c
Update README.md
wdotsol Feb 18, 2025
958c2ea
Update README.md
wdotsol Feb 18, 2025
85a76a9
Update README.md
wdotsol Feb 18, 2025
92efc1e
Update README.md
wdotsol Feb 18, 2025
a2cf546
Update README.md
wdotsol Feb 18, 2025
c6bf5c1
Update README.md
wdotsol Feb 18, 2025
cde452c
Update README.md
wdotsol Feb 18, 2025
2a1a841
Update README.md
wdotsol Feb 18, 2025
3446c05
Update README.md
wdotsol Feb 18, 2025
d803cc1
Update README.md
wdotsol Feb 18, 2025
8d74e27
Update README.md
wdotsol Feb 18, 2025
f8c4b34
Update README.md
wdotsol Feb 18, 2025
bc2d15a
Update README.md
wdotsol Feb 18, 2025
6c997aa
Merge pull request #1 from Se76/testing-x-williams and Se76/testing
Se76 Feb 19, 2025
19f82e4
Update pk in owner.json
NkamaWilliams Feb 20, 2025
990d260
anchor instruction
Timadigwe Feb 21, 2025
714a47c
updated logic
Timadigwe Feb 24, 2025
e9043d8
anchor error fix
Timadigwe Feb 26, 2025
febed75
top-up instruction added
Timadigwe Feb 27, 2025
4be701d
merged testing-x-williams into delegation_service
Timadigwe Feb 28, 2025
8fce896
add settler
Se76 Mar 3, 2025
8c4c8d4
add final changes to the rollup
Se76 Mar 5, 2025
ce5dcd3
Merge branch 'main' into final_sol_transfers_branch
Se76 Mar 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ 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/
#.idea/
.DS_Store
rollup_client/.json
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
## 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 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.

## Why would Solana need a rollup?
Rollups can enhance Solana by:
- **Increasing Throughput:** Offload transactions from the main chain.
- **Lower fees:** Batch transactions to lower costs.
- **Flexibility:** Allow for customized transaction processing without changing Solana’s core.

## Currently building:
- **Support for SPL tokens**
- **Support for all types of transactions**

## 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(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

**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.
- 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.

**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.
1 change: 1 addition & 0 deletions rollup_client/keys/receiver.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
�˽��S�S=�G�i�?v�������s�g���Xw�����O���h3��7��-Uw�K
Binary file added rollup_client/keys/sender.json
Binary file not shown.
1 change: 1 addition & 0 deletions rollup_client/mykey_1.json
Original file line number Diff line number Diff line change
@@ -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]
1 change: 1 addition & 0 deletions rollup_client/owner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[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]
34 changes: 34 additions & 0 deletions rollup_client/src/bin/generate_keys.rs
Original file line number Diff line number Diff line change
@@ -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");
}
168 changes: 111 additions & 57 deletions rollup_client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ 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},
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)]
Expand All @@ -26,66 +26,120 @@ pub struct GetTransaction {
pub get_tx: String,
}

#[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 rpc_client = RpcClient::new("https://api.devnet.solana.com".into());

let ix =
system_instruction::transfer(&keypair2.pubkey(), &keypair.pubkey(), 1 * LAMPORTS_PER_SOL);
let tx = Transaction::new_signed_with_payer(
&[ix],
Some(&keypair2.pubkey()),
&[&keypair2],
rpc_client.get_latest_blockhash().await.unwrap(),
);
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "status", content = "data")]
pub enum TransactionResponse {
Success { message: String },
Error { message: String },
}

// let sig = Signature::from_str("3ENa2e9TG6stDNkUZkRcC2Gf5saNMUFhpptQiNg56nGJ9eRBgSJpZBi7WLP5ev7aggG1JAXQWzBk8Xfkjcx1YCM2").unwrap();
// let tx = rpc_client.get_transaction(&sig, UiTransactionEncoding::Binary).await.unwrap();
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 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());

// Print initial balances
let sender_balance = rpc_client.get_balance(&sender.pubkey()).await?;
let receiver_balance = rpc_client.get_balance(&receiver.pubkey()).await?;
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 for sender
println!("\nInitializing delegation service for sender...");
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::<HashMap<String, String>>()
.await?;

println!("{test_response:#?}");

let rtx = RollupTransaction {
sender: "Me".into(),
sol_transaction: tx,
};

// let serialized_rollup_transaction = serde_json::to_string(&rtx)?;

let submit_transaction = client
.post("http://127.0.0.1:8080/submit_transaction")
.json(&rtx)
let response = client
.post("http://127.0.0.1:8080/init_delegation_service")
.body(sender.to_bytes().to_vec())
.send()
.await?;
// .json()
// .await?;
println!("Sender delegation service init response: {:?}", response.text().await?);

println!("{submit_transaction:#?}");
let mut hasher = Hasher::default();
hasher.hash(bincode::serialize(&rtx.sol_transaction).unwrap().as_slice());
// Wait for initialization
tokio::time::sleep(Duration::from_secs(2)).await;

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())]))
// Initialize delegation service for receiver
println!("\nInitializing delegation service for receiver...");
let response = client
.post("http://127.0.0.1:8080/add_delegation_signer")
.body(receiver.to_bytes().to_vec())
.send()
.await?
.json::<HashMap<String, String>>()
.await?;

println!("{tx_resp:#?}");

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, 9, -10, 1, -10, 4, -3, 9, -6];
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: tx,
};

let response = client
.post("http://127.0.0.1:8080/submit_transaction")
.json(&rtx)
.send()
.await?;

println!("Transaction {} response: {:?}", i + 1, response.text().await?);
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(())
}

async fn gen_transfer_tx(path1: String, path2: String, amount: u64) -> Transaction {
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 * (LAMPORTS_PER_SOL / 10));
Transaction::new_signed_with_payer(
&[ix],
Some(&keypair2.pubkey()),
&[&keypair2],
rpc_client.get_latest_blockhash().await.unwrap(),
)
}
1 change: 1 addition & 0 deletions rollup_client/testkey.json
Original file line number Diff line number Diff line change
@@ -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]
12 changes: 11 additions & 1 deletion rollup_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,18 @@ 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"
solana-system-program = "2.0.7"
env_logger = "0.11.5"
log = "0.4.22"
anyhow = "1.0.86"
crossbeam = "0.8.4"
async-channel = "2.3.1"
async-channel = "2.3.1"
bincode = "1.3.3"
<<<<<<< HEAD
borsh = "0.10"
sha1 = "=0.10.0"
digest = "=0.10.7"
sha2 = "0.10"
=======
>>>>>>> main
Loading