상세 컨텐츠

본문 제목

Go 언어를 기반으로한 블록체인 개발공부(Network) - Part 2

Programming Language/Go

by Yongari 2023. 2. 9. 17:00

본문

요구되는 사전 지식

TCP : 위키 링크

HTTP : 위키 링크

 

 

Part1편에서는 기본적인 블록체인 개발을 Go로 구현했는데요 이번에는 로컬호스트를 이용해서 터미널을 다른 노드라고 생각하고 로컬 네트워크에서 블록체인을 구현하는 방법을 공부해봤습니다. 로컬네트워크 말고 다른 네트워크와 통신하는 블록체인을 구현하면 좋겠지만 현재 그정도 지식은 없어서 하지 못했습니다. 

 

네트워크 실습 로직은 다음과 같습니다.

 

  1. 터미널 2에서 터미널 1로 TCP 통신을 통해  연결한다.  (리눅스 커맨드 nc를 이용합니다.) 
  2. 터미널2에서 BPM 입력을 통해 터미널1(go 블록체인 서버)에 연결해서 블록을 생성한다.
  3. 터미널1에서 생성된 블록정보를  검사하고 다시 터미널2로 전달한다. 
  4. 모든 터미널에서 생성된 블록이 동기화 된다. 

 

 

환경 설정

.env 파일에 다음 포트를 설정 다른 포트번호를 설정해도 괜찮습니다.

ADDR=9000

 

dump를 위해서 spew, .env를 불러오기 위해 godotenv 패키지가 필요합니다. 
실습 폴더에서 다음 커맨드를 입력해주세요.

 

go get github.com/davecgh/go-spew/spew
go get github.com/joho/godotenv

 

 

네트워크 실습 코드

package main

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"log"
	"net"
	"os"
	"strconv"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/joho/godotenv"
)

// Block represents each 'item' in the blockchain
type Block struct {
	Index     int
	Timestamp string
	BPM       int
	Hash      string
	PrevHash  string
	
}


// Blockchain is a series of validated Blocks
var Blockchain []Block
var bcServer chan []Block 


// SHA256 hashing
func calculateHash(block Block) string {
	record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash
	h := sha256.New()
	h.Write([]byte(record))
	hashed := h.Sum(nil)
	return hex.EncodeToString(hashed)
}

// create a new block using previous block's hash
func generateBlock(oldBlock Block, BPM int)(Block, error) {
    

	var newBlock Block


	t := time.Now()


	newBlock.Index = oldBlock.Index + 1
	newBlock.Timestamp = t.String()
	newBlock.BPM = BPM
	newBlock.PrevHash = oldBlock.Hash
	newBlock.Hash = calculateHash(newBlock)


	return newBlock, nil
}
// make sure block is valid by checking index, and comparing the hash of the previous block
func isBlockValid(newBlock, oldBlock Block) bool {
	if oldBlock.Index+1 != newBlock.Index {
		return false
	}


	if oldBlock.Hash != newBlock.PrevHash {
		return false
	}


	if calculateHash(newBlock) != newBlock.Hash {
		return false
	}


	return true
}

// make sure the chain we're checking is longer than the current blockchain
func replaceChain(newBlocks []Block) {
	if len(newBlocks) > len(Blockchain) {
		Blockchain = newBlocks
	}
}




func main(){
	err := godotenv.Load()
	if err != nil {
		log.Fatal(err)
	}

	bcServer = make(chan []Block)

	//create genesis block 
	t:= time.Now()
	genensisBlock := Block{0, t.String(), 0, "", ""}
	spew.Dump(genensisBlock)//debuging 용도
	Blockchain = append(Blockchain, genensisBlock) //블록체인 배열 변수에 제네시스 블록 삽입 


	//start TCP and serve TCP server
	server, err := net.Listen("tcp","localhost:"+os.Getenv("ADDR"))
	if err != nil{
		log.Fatal(err)
	}
	//connection이 더 이상 필요 없을 때 종료하기 위해 사용함 
	defer server.Close()


	//새로운 connection들을 받을 수 있게 무한 루프 생성 
	for {
		conn, err := server.Accept()
		if err != nil{
			log.Fatal(err)
		}
		//동시에 각 connection들을 분리해서 다룰 수 있도록 하기
		//go 루틴 go handleConn을 통해 해결
		go handleConn(conn)
	}


	
}
//한 개의 인자 (net.Conn 필요)
func handleConn(conn net.Conn){
	//각 connection 종료 가능 
	defer conn.Close()
	

	//BPM을 입력한다.
	io.WriteString(conn, "Enter a new BPM:")
    
		//새로운 스캐너를 생성합니다.
        scanner := bufio.NewScanner(conn)
    

        // take in BPM from stdin and add it to blockchain after conducting necessary validation
        go func() {
			//for scanner.Scan() 반복문은 Go 루틴안에 작성합니다. 그러면 다른 connection들과 분리하여 수행합니다.
            for scanner.Scan() {
                bpm, err := strconv.Atoi(scanner.Text())
                if err != nil {
                    log.Printf("%v not a number: %v", scanner.Text(), err)
                    continue
                }

				//이전에 생성한 gnerateBlock, isBlockValid 그리고 replaceChanin 함수들을 이용하여 데이터를 가진 블록을 생성한다.
                newBlock, err := generateBlock(Blockchain[len(Blockchain)-1], bpm)
                if err != nil {
                    log.Println(err)
                    continue
                }
                if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
                    newBlockchain := append(Blockchain, newBlock)
                    replaceChain(newBlockchain)
                }
				// 새로운 블록체인을 채널에 넣는 것을 의미합니다.		
				//우리가 다른 노드에 알려주기 위해 만든 네트워크에 블록체인을 넣는다.
                bcServer <- Blockchain
				//사용자가 새로운 BPM을 입력할 수 있도록 한다.
                io.WriteString(conn, "\nEnter a new BPM:")
            }
        }()

		// simulate receiving broadcast
	// simulate receiving broadcast
	go func() {
		for {
			time.Sleep(30 * time.Second)
			output, err := json.Marshal(Blockchain)
			if err != nil {
				log.Fatal(err)
			}
			io.WriteString(conn, string(output))
		}
	}()


	for _ = range bcServer {
		spew.Dump(Blockchain)
	}

}

 

 

터미널1과 터미널2 출력 결과

 

터미널1( go 블록체인 서버 )

 

터미널2( 터미널1과 연결할 노드)

 

 

 

 

참고 링크:

 

한글 : 링크 

 

[블록체인 개발 공부] 블록체인 개발하기 PART 2 : Networking — Steemit

이 내용은 CoralHeath 블로그의 내용을 번역 한 것 입니다. https://medium.com/@mycoralhealth/part-2-networking-code-your-own-blockchain-in-less-than-200-lines-of-go-17fe1dad46e1 지난 포스팅에서는 기본적인 블록체인을 코딩

steemit.com

영문 : 링크 (원본)

 

관련글 더보기