[blockchain-027] Go implementation of a minimalist verification version of paxos

1. References

"Analysis of Blockchain Core Algorithms", page 15.

2. Three files: main.go, service.go, client.go

You can compile the three files in the same directory without using third-party libraries. Compiled and executable. Client and server are in the same process.

3.main.go

package main

import (
	"time"
)

func main() {
	done := make(chan int)

	go startServer()
	time.Sleep(1000 * time.Millisecond)
	go runClient()

	<-done
}

4.service.go

package main

import (
	"fmt"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"strconv"
	"strings"
)

//server input parameters
type Args struct {
	StrParams string
}

//Server output parameters
type Reply struct {
	StrResult string
}

// structure of the service
type PaxosService struct {
	//process parameters
	Host string `The host name or ip address where the process is located`
	Port string `The port number used by this process`

	//paxos parameter
	T_max int `Currently issued maximum ticket number`
	C string `Currently stored command`
	T_store int `Store ticket for command C`
}

// service test function
func (ps *PaxosService) Test1(args *Args, reply *Reply) error {
	reply.StrResult = args.StrParams + "123"
	return nil
}

func (ps *PaxosService) Process(args *Args, reply *Reply) error {
	reply.StrResult = args.StrParams + "123"
	//segment parameters
	params := strings.Split(args.StrParams, ":")

	// output
	//	for _, p := range params {
	//		fmt.Println(p)
	//	}
	//reply according to the parameters
	switch params[2] {
	case "require_ticket":
		// Phase 1 client request ticket
		fmt.Println("Phase 1 Client Request Ticket")
		require_t, err := strconv.Atoi(params[3])
		if err != nil {
			fmt.Println("Failed to convert string to integer", err, params[3])
			return err
		}
		fmt.Println("Phase 1 ,t=", require_t)
		if require_t > ps.T_max {
			ps.T_max = require_t
			reply.StrResult = ps.Host + ":" + ps.Port + ":" + "response_ticket" + ":" + "ok" + ":" + ps.C
		}
	case "require_propose":
		fmt.Println("Phase 2 client request propose")
		require_t, err := strconv.Atoi(params[3])
		if err != nil {
			fmt.Println("Failed to convert string to integer", err, params[3])
			return err
		}
		fmt.Println("require_t=", require_t, ", ps.T_max=", ps.T_max)
		if require_t == ps.T_max {
			ps.C = params[4]
			ps.T_store = require_t
			reply.StrResult = ps.Host + ":" + ps.Port + ":" + "response_propose" + ":" + "success"
		}
	case "require_commit":
		fmt.Println("Phase 3 client request commit")
		require_t, err := strconv.Atoi(params[3])
		if err != nil {
			fmt.Println("Failed to convert string to integer", err, params[3])
			return err
		}
		if require_t == ps.T_store {
			if params[4] == ps.C {
				reply.StrResult = ps.Host + ":" + ps.Port + ":" + "response_propose" + ":" + "run" + ":" + params[4]
			}
		}
	default:
		fmt.Println("The command is unknown, the stage cannot be confirmed, return fail")
		reply.StrResult = ps.Host + ":" + ps.Port + ":" + "fail"
	}

	return nil
}

// start the service
func startServer() {
	fmt.Println("start server...")
	ps: = PaxosService {
		Host:    "127.0.0.1",
		Port:    "1234",
		T_max:   0,
		C:       "none",
		T_store: 0,
	}
	server := rpc.NewServer()
	server.Register(&ps)
	server.HandleHTTP(rpc.DefaultRPCPath, rpc.DefaultDebugPath)
	listener, e := net.Listen("tcp", ":1234")
	if e != nil {
		log.Fatal("listen error:", e)
	}
	fmt.Println("server running...")
	for {
		if conn, err := listener.Accept(); err != nil {
			log.Fatal("accept error: " + err.Error())
		} else {
			log.Printf("new connection established\n")
			go server.ServeCodec(jsonrpc.NewServerCodec(conn))
		}
	}
}

5.client.go

package main

import (
	"fmt"
	"log"
	"net"
	"net/rpc/jsonrpc"
)

//Run the client once, submit the parameters, and get the result
func runClient () {
	client, err := net.Dial("tcp", "127.0.0.1:1234")
	if err != nil {
		log.Fatal("dialing:", err)
	}

	//The first stage request ticket
	args := &Args{"127.0.0.1:1234:require_ticket:34"}
	var reply Reply
	c := jsonrpc.NewClient(client)
	err = c.Call("PaxosService.Process", args, &reply)
	if err != nil {
		log.Fatal("PaxosService error:", err)
	}
	fmt.Println("阶段1 response:", reply.StrResult)

	//The second stage propose
	args = &Args{"127.0.0.1:1234:require_propose:34:akp"}
	err = c.Call("PaxosService.Process", args, &reply)
	if err != nil {
		log.Fatal("PaxosService error:", err)
	}
	fmt.Println("阶段2 response:", reply.StrResult)

	// Phase 3 commit
	args = &Args{"127.0.0.1:1234:require_commit:34:akp"}
	err = c.Call("PaxosService.Process", args, &reply)
	if err != nil {
		log.Fatal("PaxosService error:", err)
	}
	fmt.Println("阶段3 response:", reply.StrResult)

}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324821542&siteId=291194637
Recommended