TIL 14 - Golang으로 블록체인 만들기 2

프동프동·2023년 2월 10일
0

TIL

목록 보기
14/46
post-thumbnail

Refactoring, Singleton 적용

  • 기존 프로그램에 문제가 있다.
    • main함수에 모든 것을 작성하였으며
    • 하나의 함수에서 블록을 생성하고
    • 블록을 해시하고
    • 새로운 블록을 추가하는 등
  • 코드를 리펙토링하고 sington을 적용해본다.

Sington 적용

  • 소스코드
    • main.go

      package main
      
      import "coin/exam03/blockchain"
      
      func main() {
      	chain := blockchain.GetBlockchain()
      }
    • blockchain/blockchain.go

      package blockchain
      
      import "sync"
      
      type block struct {
      	data     string
      	hash     string
      	prevHash string
      }
      type blockchain struct {
      	blocks []block
      }
      
      // 블록체인을 공유하고 초기화하는 작업
      // Singleton pattern
      // blockchain의 단 하나의 instance만을 공유하는 방법
      
      // 공유하지 않으므로 private 선언
      var b *blockchain
      
      // 위 변수의 instance를 대신해서 드러내는 function 생성
      // -> 다른 패키지에서 우리의 blockchain이 어떤식으로 생성될지 제어 가능하다는 의미
      func GetBlockchain() *blockchain {
      	// b가 초기화되었는지 확인 후 처음이자 마지막으로 초기화 진행
      	// nil이면 아직 생성되지 않아 새로 생성한다는 의미
      	// 두번째로 부르는 순간부터 생성, 초기화는 진행되지 않는다.
      	if b == nil {
      		// "=" 를 통해 b를 새로 생성하지 않고 대입해준다.
      		b = &blockchain{}
      	}
      	return b
      }

Refactoring

  • 소스코드
    • main.go
      package main
      
      import "coin/exam04/blockchain"
      
      func main() {
      	chain := blockchain.GetBlockchain()
      }
    • blockchain/blockchain.go
      package blockchain
      
      import (
      	"crypto/sha256"
      	"fmt"
      	"sync"
      )
      
      type block struct {
      	data     string
      	hash     string
      	prevHash string
      }
      type blockchain struct {
      	// block 타입의 포인터 슬라이스
      	blocks []*block
      }
      
      var b *blockchain
      
      // 단 한번만 실행되게 하기 위함
      var once sync.Once
      
      // Hash를 계산해주는 함수
      func (b *block) calculateHash() {
      	hash := sha256.Sum256([]byte(b.data + b.prevHash))
      	b.hash = fmt.Sprintf("%x", hash)
      }
      
      // 이전 해시값을 생성해서 가져오는 함수
      func getLastHash() string {
      	totalBlocks := len(GetBlockchain().blocks)
      	// 블록의 개수가 0개면  마지막 Hash 값이 없기 떄문에 아무것도 반환하지 않는다.
      	if totalBlocks == 0 {
      		return ""
      	}
      	// 마지막 블록체인의 Hash를 반환해준다.
      	return GetBlockchain().blocks[totalBlocks-1].hash
      }
      
      func createBlock(data string) *block {
      	newBlock := block{data, "", getLastHash()}
      	newBlock.calculateHash()
      	return &newBlock
      }
      
      func GetBlockchain() *blockchain {
      	if b == nil {
      		// 단 한번만 실행된다.
      		// 병렬적으로 처리하든 뭘하든 해당 함수는 한번만 실행됨을 의미
      		once.Do(func() {
      			// 블록체인을 하나 생성하고
      			b = &blockchain{}
      			// 하나의 블록을 추가한다.
      			b.blocks = append(b.blocks, createBlock("Genesis Block"))
      		})
      	}
      	return b
      }

block 추가 및 정보 가져오기

앞서 만든 코드를 이용하여 blockchain에 block을 등록하고 block 정보를 가져오는 기능 구현

  • 소스 코드
    • main.go
      package main
      
      import (
      	"coin/exam05/blockchain"
      	"fmt"
      )
      
      func main() {
      	chain := blockchain.GetBlockchain()
      	chain.AddBlock("Second Block")
      	chain.AddBlock("Third Block")
      	chain.AddBlock("Fourth Block")
      	for _, block := range chain.AllBlocks() {
      		fmt.Printf("Data: %s\n", block.Data)
      		fmt.Printf("Hash: %s\n", block.Hash)
      		fmt.Printf("Prev Hash: %s\n", block.PrevHash)
      	}
      }
    • blockchain/blockchain.go
      package blockchain
      
      import (
      	"crypto/sha256"
      	"fmt"
      	"sync"
      )
      
      type block struct {
      	Data     string
      	Hash     string
      	PrevHash string
      }
      type blockchain struct {
      	// block 타입의 포인터 슬라이스
      	blocks []*block
      }
      
      var b *blockchain
      
      var once sync.Once
      
      func (b *block) calculateHash() {
      	hash := sha256.Sum256([]byte(b.Data + b.PrevHash))
      	b.Hash = fmt.Sprintf("%x", hash)
      }
      
      func getLastHash() string {
      	totalBlocks := len(GetBlockchain().blocks)
      	if totalBlocks == 0 {
      		return ""
      	}
      	return GetBlockchain().blocks[totalBlocks-1].Hash
      }
      
      func createBlock(data string) *block {
      	newBlock := block{data, "", getLastHash()}
      	newBlock.calculateHash()
      	return &newBlock
      }
      
      // export 함수
      func (b *blockchain) AddBlock(data string) {
      	b.blocks = append(b.blocks, createBlock(data))
      }
      
      func GetBlockchain() *blockchain {
      	if b == nil {
      		once.Do(func() {
      			b = &blockchain{}
      			b.AddBlock("Genesis Block")
      		})
      	}
      	return b
      }
      
      // 사용자에게 field를 드러내주는 function(singleton의 철학)
      func (b *blockchain) AllBlocks() []*block {
      	return b.blocks
      	// return GetBlockchain().blocks
      }

기존에 private하게 만들어둔 block 정보를 public하게 만들어 준 후 AllBlocks() 함수를 통해 데이터를 읽어왔다. 이부분은 추후 수정

profile
좋은 개발자가 되고싶은

0개의 댓글