This project is a code collaboration tool similar to Github except it runs on a on a peer-to-peer (P2P) distributed network architecture. It is built using Go and the libp2p framework.
Peer Groups:
- Each repository is stored on a group of N nodes.
- Each Peer group has a leader and a lock.
-
gRPC Server: The core API of the network that handles requests such as repo initialization and lock acquisitions through remote procedure calls.
-
Stream Handlers: Built over TCP, facilitate communication between the peers in the network.
-
Git Daemon: Uses the git protocol for various features such as push and pull.
-
BadgerDB: Acts as a cache to store peer connection information upon discovery.
-
Distributed Hash Table (DHT): Used for Peer Discovery and mapping which peers own which repo.
Active replication when initializing repositories:
- gRPC server collects N Alive peers from its peer store or tries to discover new peers
- Send an Init request to all N peers
Passive replication pushing to repositories:
- Leader recives new code from client
- Pushes the code to each of the follower peers
- Redundancy: Storing DHT and repos across multiple peers.
- ISR List: A list of peers that have the latest code version.
- BadgerDB: Embedded KV store that stores the connection information of different peers in the network.
fault_tolerance.mov
Consistency model: Strict order only on the write operations per repo.
Bully Leader Election Algorithm:
- One leader is chosen for every repository in order to have a central authority on push operations.
- Bully leader election: prioritizing peers based on highest ID for selection.
- Only the peers stored in the ISR list are candidates for the leader election.
Steps to push to a repo:
- Contact the master peer (for that repo) and request to push code.
- A lock is set on the peer giving the peer permission to push it’s changes.
- Once the master receives those changes, it forwards those changes to all the other peers that are storing that repo.
- Master node updates ISR list with IDs of successful peers.
- Master node updates repository version and stores updated record in DHT.
The video below shows an example of a push operation:
consistency.mov
- Go (version 1.20)
git clone [email protected]:Ahmed-I-Abdullah/p2p-code-collaboration.git
cd p2p-code-collaboration
You can install the project's dependecies using the following command:
go mod tidy
You can build the project using the following command:
go build -o p2p ./cmd/main.go
-
rendezvous: Specifies a unique identifier for a P2P network group. Nodes with the same rendezvous string can discover and connect to each other. (default: "meet me here")
-
peer: Adds a peer's multiaddress to the list of bootstrap nodes. This enables a node to connect to existing peers for network bootstrapping.
-
listen: Specifies the multiaddress for the node to listen on. Other nodes can connect to this address to establish P2P connections.
-
is_bootstrap: Indicates whether the node serves as a bootstrap node. Bootstrap nodes facilitate the initial connection establishment for new nodes joining the network. (default: false)
-
grpcport: Specifies the port for the gRPC server. gRPC is used for communication between nodes in the P2P network.
-
gitport: Specifies the port for the Git daemon. This port enables Git operations over the P2P network.
-
repos_dir: Specifies the directory from which Git repositories are served. This directory holds the shared code repositories accessible to nodes in the P2P network. (default: "./repos")
-
priv_key: Specifies the file path to store the peer's private key. (default: "./.priv_key")
To start a bootstrap node, run the following command:
./p2p -listen /ip4/<your_ip>/tcp/<node_port> -rendezvous test -is_bootstrap true
Once the bootstrap node is running, you can connect to it using the same binary with additional flags:
./p2p -listen /ip4/<your_ip>/tcp/<node_port> -rendezvous test -grpcport <grpc_port> -gitport <git_daemon_port> -priv_key <private_key_path> -repos_dir <repos_dir_path> -peer <bootstrap_address>
Replace placeholders <your_ip>, <node_port>, <grpc_port>, <git_daemon_port>, <repos_dir_path>, and <bootstrap_address> with appropriate values.
For example:
./p2p -listen /ip4/172.20.10.12/tcp/6667 -rendezvous test -grpcport 3000 -gitport 3001 -priv_key ./.priv_key -repos_dir repos1 -peer /ip4/172.20.10.12/tcp/6666/p2p/12D3KooWKV9yGUYG5KBwmj5hge332gYzKhwaJ9RjBJX2HE86zYVt
- Run
git-peer init (repo name)
to initialize a bare repo with that repo name - Run
git-peer pull (repo name)
to pull the latest changes from that repo name - Run
git-peer add
to add any new local changes to the staging area - Run
git-peer commit -m "(message)"
to commit any changes in the staging area - Run
git-peer push
to push all commits to the leader branch