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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@ rustCode/target/
rustCode/Cargo.lock

# Rust FFI shared libraries in Go app folder
goCode/goApp/librpc.a
goCode/goApp/rpc.dll
goCode/goApp/librpc.so
goCode/libs/*
45 changes: 45 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
GO_DIR = goCode/goApp
RUST_DIR = rustCode
LIBS_DIR = goCode/libs

# Linux specifics
RUST_LIB_LINUX = $(RUST_DIR)/target/release/librpc.so

# Windows specifics
RUST_LIB_WINDOWS_DLL = $(RUST_DIR)/target/release/rpc.dll
RUST_LIB_WINDOWS_LIB = $(RUST_DIR)/target/release/rpc.dll.lib

# build Rust library
RUST_BUILD_CMD = cargo build --release

allLinux: build_rust copy_libs_linux run_go

allWindows: build_rust copy_libs_windows run_go

build_rust:
@echo "Building Rust library for Linux..."
cd $(RUST_DIR) && $(RUST_BUILD_CMD)

copy_libs_linux:
@echo "Copying librpc.so to Go libs folder (Linux)..."
cp $(RUST_LIB_LINUX) $(LIBS_DIR)

copy_libs_windows:
@echo "Copying rpc.dll and rpc.dll.lib to Go libs folder (Windows)..."
cp $(RUST_LIB_WINDOWS_DLL) $(LIBS_DIR)/rpc.dll
cp $(RUST_LIB_WINDOWS_LIB) $(LIBS_DIR)/librpc.a # Renaming .lib to .a

run_go:
@echo "Running Go application..."
cd $(GO_DIR) && go run main.go

run_rust:
@echo "Running Rust code..."
cd $(RUST_DIR) && cargo run

clean:
@echo "Cleaning up..."
rm -rf $(RUST_DIR)/target
rm -f $(LIBS_DIR)/librpc.so
rm -f $(LIBS_DIR)/rpc.dll
rm -f $(LIBS_DIR)/librpc.a
187 changes: 99 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
# BlockchainSimulations
This repository is dedicated to experimenting with blockchain technologies.
This repository is dedicated to experimenting with blockchain technologies using a hybrid architecture that involves both Go and Rust. It focuses on cross-language communication via FFI (Foreign Function Interface) to fetch blockchain data, analyze and simulate transactions.

## Project Structure
```
./
├── README.md*
├── goCode/
│ ├── go.mod*
│ ├── go.sum*
│ ├── goApp/
│ │ ├── librpc.a*
│ │ ├── main.go*
│ │ └── rpc.dll*
│ └── internal/
│ ├── config/
│ │ └── config.go*
│ ├── rpc/
│ │ ├── rpc.go*
│ │ └── types.go*
│ ├── transaction/
│ │ ├── balance.go*
│ │ └── transaction.go*
│ └── utilis/
│ └── utilis.go*
└── rustCode/
├── Cargo.lock*
├── Cargo.toml*
└── src/
├── config/
│ └── mod.rs*
├── lib.rs*
├── main.rs*
├── models/
│ └── mod.rs*
└── rpc/
├── fetch.rs*
└── mod.rs*
``` bash
.
├── .github
│ └── workflows
│ └── check-env.yml
├── .gitignore
├── goCode
│ ├── .env # .env file
│ ├── goApp
│ │ └── main.go
│ ├── go.mod
│ ├── go.sum
│ ├── internal
│ │ ├── config
│ │ │ └── config.go
│ │ ├── rpc
│ │ │ ├── rpc.go
│ │ │ └── types.go
│ │ ├── transaction
│ │ │ ├── balance.go
│ │ │ └── transaction.go
│ │ └── utilis
│ │ └── utilis.go
│ └── libs
│ └── (librpc.so) or (rpc.dll & librpc.a) # Rust library used in Go via FFI
├── Makefile
├── README.md
└── rustCode
├── Cargo.lock
├── Cargo.toml
└── src
├── config
│ └── mod.rs
├── lib.rs
├── main.rs
├── models
│ └── mod.rs
├── rpc
│ ├── fetch.rs
│ └── mod.rs
└── utils
├── functions.rs
├── mod.rs
└── prints.rs
```
## Setup Instructions

Expand All @@ -47,71 +57,71 @@ Before you can run the program, ensure you have the following installed:

2. **Rust**: Ensure you have the Rust toolchain installed, which includes `cargo` and `rustc`. You can install it from the official [Rust website](https://www.rust-lang.org/learn/get-started).

3. **Geth**: The **Go Ethereum** client is required for the Go program to interact with the Ethereum blockchain. You can download it from the official [Geth website](https://geth.ethereum.org/downloads/).

4. **.env file**: Ensure the `.env` file is properly configured in the `goCode` folder with the following variables:
3. **.env file**: Ensure the `.env` file is properly configured in the `goCode` folder with the following variables:

```env
PRIVATE_KEY=your_private_key
RECIPIENT_ADDRESS=recipient_address
RPC_URL=rpc_url_of_your_choice
PRIVATE_KEY = your_private_key
RECIPIENT_ADDRESS = recipient_address
RPC_TESTNET_URL = rpc_url_of_your_choice (Tenderly - recommended)
RPC_MAINNET_URL = rpc_url_of_your_choice (Infura recommended)
```

5. **Tenderly Virtual Network** (Optional but Recommended)

- The project runs on the **Tenderly Virtual Network**, which simulates Ethereum blockchain interactions for testing and safe experimentation. Tenderly provides a controlled environment where you can simulate and test Ethereum transactions, making it ideal for development and experimentation without risking real assets.

- You can connect to the Tenderly Virtual Network by configuring the appropriate RPC URL in the `.env` file.



### Building and Running the Program on Windows
**Rust Part (FFI Library)**
- Navigate to the `rustCode` folder.
- Run the following command to build the Rust FFI library:
```
cargo build --release
```
- for **Ubuntu/Linux** -> This will generate librpc.so in the rustCode/target/release folder.
- Then copy them to `goCode/goApp` folder as
```
cp target/release/librpc.so ../goCode/goApp/
```

- for **Windows** -> This will generate **rpc.dll** and **rpc.dll.lib** in `rustCode/target/release` folder
- Then copy them to `goCode/goApp` folder as
- rpc.dll -> **rpc.dll**
- rpc.dll.lib -> **librpc.a** *(renamed)*

**Go Part**
- Navigate to the `goCode/goApp` folder.
- Then run
```go run main.go```

You can allso run just rust part of rust code trough command `cargo run` in `rustCode` folder


- **[Tenderly](https://tenderly.co/about-us)** is used for testnet interactions, providing a simulated blockchain environment ideal for testing and experimentation.
- **[Infura](https://www.infura.io/)** is used for mainnet data, enabling access to real-world blockchain data via a robust API service.
- **[Geth](https://geth.ethereum.org/downloads/)**. (if using locally): If running a local Geth node, set the RPC URL to your configured port. Ensure Geth is synced and running with RPC enabled.

## Building and Running the Program

You can build and run the program for both Linux and Windows using the Makefile. This automates the process of building the Rust FFI library, copying the appropriate library files, and running the Go application.

1. Navigate to the root of the project.
2. Run the following command to build and run the program:
- **Build** and **run** for **Linux**
```
make allLinux
```
> This will:
> - Build the Rust library (librpc.so)
> - Copy the librpc.so file to the goCode/libs folder
> - Run the Go application

- **Build** and **run** for **Windows**
```
make allWindows
```
>This will:
> - Build the Rust library (rpc.dll and rpc.dll.lib)
> - Copy the rpc.dll to goCode/libs/rpc.dll and rpc.dll.lib to goCode/libs/librpc.a
> - Run the Go application

## Makefile Commands
- `make allLinux`: Builds the Rust library for Linux, copies the library to the Go app's libs folder, and runs the Go application.
- `make allWindows`: Builds the Rust library for Windows, copies the library to the Go app's libs folder, and runs the Go application.
- `make clean`: Cleans up the build artifacts, including the `target` directory for Rust and the `librpc.so/rpc.dll` in the Go `libs` folder.
- `make run_rust`: Runs only the Rust code (`cargo run`).
- `make run_go`: Runs only the Go application (`go run main.go`).

## Functionalities

1. **Go Program**:
- **Fetches the latest block number** from the blockchain using the provided RPC URL via a standard RPC connection. It sends an RPC request to the Ethereum node and retrieves the latest block's details, including transactions.
- **Sets a fake balance** for a recipient address using the **Geth client** in the Go program, by calling the `tenderly_setBalance` method to simulate a blockchain environment and test transaction functionalities.
- **Sends a transaction** to the recipient address with a specified amount of Ether, utilizing the **Geth client** to interact directly with the Ethereum blockchain. This includes creating and signing the transaction using a private key and then broadcasting it to the network.
- **Uses the FFI mechanism** to call the Rust program and fetch transaction data from the latest block, enabling cross-language communication for blockchain data fetching and analysis.

- **Sets a fake balance** for a recipient address using the Tenderly-specific `tenderly_setBalance` method via RPC to simulate a blockchain environment and test transaction functionalities.
- **Sends a transaction** to the recipient address with a specified amount of Ether, utilizing the go-ethereum library (`ethclient`) to connect to an RPC endpoint, create, sign, and broadcast the transaction. This interacts with the blockchain via RPC.
- **Uses the FFI mechanism** to call the Rust program and fetch transaction data from the latest block, enabling **cross-language communication** for blockchain data fetching and analysis.
- **Fetches maximum gas transactions** from the last 5 blocks by calling Rust via **FFI**, which retrieves and analyzes transaction receipts to identify the **highest gas-used** transactions per block, including their **percentage** of the block's total gas.

2. **Rust Program**:
- The Rust Program can also **fetch the latest block number** directly as Go Program
- The Rust library (`rpc.dll`) exposes the function `fetch_transactions`, which retrieves **transaction data** from the latest block on the blockchain. This function is called from the Go program through FFI.
- The Rust program can independently **fetch the latest block number** and details directly via RPC, similar to the Go program.
- The Rust library (`rpc.dll` or `librpc.so`) exposes functions like `fetch_transactions`, which retrieves transaction data from the latest block on the blockchain. This function is called from the Go program through FFI.
- Additionally, it exposes `fetch_max_tx_per_last_5_blocks`, which fetches the last 5 blocks, retrieves transaction receipts, and identifies the transaction with the maximum gas usage in each block, calculating its percentage of the block's gas limit. This data is returned as JSON for processing in Go.
- The Rust program can run standalone to fetch and analyze blocks from both testnet and mainnet, printing block info, transaction details, and summaries of max gas transactions.

In summary:
- The **Go program** uses **Geth client** for sending transactions and setting fake balances, directly interacting with the Ethereum blockchain.
- The **Rust library** is used for fetching blockchain data via RPC calls for getting transaction details from the latest block.

- The **Go program** handles blockchain interactions like setting fake balances (via Tenderly RPC), sending transactions (via go-ethereum RPC client), and fetching block data, all through external RPC endpoints without requiring a local Geth client.
- The **Rust library** is used for fetching and analyzing blockchain data via RPC calls, including transaction details and gas usage summaries from multiple blocks. It communicates with Go through FFI for integrated functionality.


### FFI Usage:
The project uses **FFI (Foreign Function Interface)** to enable communication between Go and Rust. Go interacts with the Ethereum blockchain using the **Geth client** for transaction creation and fake balance setting. Rust, on the other hand, fetches transaction data from the latest block through the `rpc.dll` library. Go calls Rust functions via FFI to retrieve blockchain data.
The project uses **FFI (Foreign Function Interface)** to enable communication between Go and Rust. Go interacts with the Ethereum blockchain using RPC endpoints (via the go-ethereum library) for transaction creation and fake balance setting. Rust, on the other hand, fetches transaction data from the latest block through the rpc.dll library. Go calls Rust functions via FFI to retrieve blockchain data.

---

Expand All @@ -122,8 +132,9 @@ The project uses **FFI (Foreign Function Interface)** to enable communication be
* **Tenderly Virtual Network**: The project runs on the **Virtual TestNets**, simulating Ethereum blockchain interactions for testing and safe experimentation.

* **Communication**:
- **Go** handles blockchain interaction (sending transactions, setting balances) using the **Geth client**.
- **Rust** fetches transaction details from the latest block via **RPC** and communicates with Go through **FFI**.
- **Go** handles blockchain interaction (sending transactions, setting balances) using RPC endpoints via the go-ethereum library.
- **Rust** fetches transaction details from the latest blocks via **RPC** and communicates with Go through **FFI**.

* **Simplify**: In this project, for the sake of simplicity and testing, the same address `RECIPIENT_ADDRESS` was used for both the sender and the recipient. This is done to simulate the transaction process without involving multiple real addresses. In a real-world scenario, the sender and recipient would have distinct addresses.


19 changes: 10 additions & 9 deletions goCode/goApp/main.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package main

/*
#cgo LDFLAGS: -L. -lrpc -Wl,-rpath=.
#cgo LDFLAGS: -L../libs -lrpc -Wl,-rpath=../libs
#include <stdlib.h>

// fetch_transactions je Rust funkcija koja vraca podatke o transakcijama iz poslednjeg bloka
char* fetch_transactions(const char* rpc_url);

// fetch_last_5_blocks je Rust funkcija koja vraca podatke o transakciji maksimalnog potrosenog gasa u poslednjih 5 blokova
char* fetch_last_5_blocks(const char* rpc_url);
// fetch_max_tx_per_last_5_blocks je Rust funkcija koja vraca podatke o transakciji maksimalnog potrosenog gasa za svaki od poslednjih 5 blokova
char* fetch_max_tx_per_last_5_blocks(const char* rpc_url);
*/
import "C"

Expand Down Expand Up @@ -37,26 +37,27 @@ func main() {
sendEth := 1
transaction.SendTransaction(rpcTestnetURL, recipientAddress, int64(sendEth), privateKeyHex)

// === Deo koji zove Rust FFI za transakcije ===
fmt.Println()

// === Deo koji zove Rust FFI ===
cRpcTestnetURL := C.CString(rpcTestnetURL)
defer C.free(unsafe.Pointer(cRpcTestnetURL))

fmt.Println("----------Rust pokrece FFI za transakcije iz poslednjeg bloka:----------")
result := C.fetch_transactions(cRpcTestnetURL)
defer C.free(unsafe.Pointer(result))

fmt.Println("----------Rust je zavrsio FFI----------")
goResult := C.GoString(result)
fmt.Println("Rust FFI: Podaci o transakcijama iz poslednjeg bloka:\n", goResult)

// === Deo koji zove Rust FFI za 5 blokova ===
fmt.Println()

cRpcMainnetURL := C.CString(rpcMainnetURL)
defer C.free(unsafe.Pointer(cRpcMainnetURL))

resultArray := C.fetch_last_5_blocks(cRpcMainnetURL)
fmt.Println("----------Rust pokrece FFI za maksimalne transakcije iz poslednjih 5 blokova:----------")
resultArray := C.fetch_max_tx_per_last_5_blocks(cRpcMainnetURL)
defer C.free(unsafe.Pointer(resultArray))
fmt.Println("----------Rust je zavrsio FFI----------")
goArray := C.GoString(resultArray)

var summaries []rpc.TxSummary
Expand All @@ -67,7 +68,7 @@ func main() {
}

for _, s := range summaries {
fmt.Printf("Blok %s | TX %s | Gas %d | %.3f%% u bloku\n",
fmt.Printf("Blok %s | MAX TX %s | Gas %d | %.3f%% u bloku\n",
s.BlockNumber, s.TxHash, s.GasUsed, s.PercentInBlock)
}
}
2 changes: 1 addition & 1 deletion goCode/internal/rpc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type Transaction struct {
}

type TxSummary struct {
BlockNumber uint64 `json:"block_number"`
BlockNumber string `json:"block_number"`
TxHash string `json:"tx_hash"`
GasUsed uint64 `json:"gas_used"`
PercentInBlock float64 `json:"percent_in_block"`
Expand Down
2 changes: 1 addition & 1 deletion rustCode/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub extern "C" fn fetch_transactions(rpc_url: *const c_char) -> *mut c_char {
}

#[no_mangle]
pub extern "C" fn fetch_last_5_blocks(rpc_url: *const c_char) -> *mut c_char {
pub extern "C" fn fetch_max_tx_per_last_5_blocks(rpc_url: *const c_char) -> *mut c_char {
let c_rpc = unsafe { std::ffi::CStr::from_ptr(rpc_url) };
let rpc_url_str = c_rpc.to_str().unwrap();

Expand Down
3 changes: 3 additions & 0 deletions rustCode/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use rpc::fetch::{fetch_latest_block, fetch_block_by_number, fetch_last_5_blocks_
use utils::{print_block_info, hex_to_u64, analyze_max_gas_transaction};
use tokio;

//use crate::utils::print_transactions;

#[tokio::main]
async fn main() {
load_testnet_env();
Expand All @@ -23,6 +25,7 @@ async fn main() {

println!("Testnet blok:");
print_block_info(&block_testnet);
//print_transactions(&block_testnet.transactions);
println!("----------------------");
println!("Mainnet blok:");
print_block_info(&block_mainnet);
Expand Down
Loading