ChainMaker.go

package service

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"sort"
	"strings"
	"time"

	"saseul/config"
	"saseul/data/bunch"
	"saseul/data/chain"
	"saseul/data/env"
	"saseul/data/mainchain"
	"saseul/data/tracker"
	"saseul/datasource/poolclient"
	"saseul/model"
	"saseul/staff/observer"
	"saseul/staff/processmanager"
	"saseul/util/clock"
	"saseul/vm/machine"
)

type ChainMaker struct {
	Description string
	Iterate     int
}

// NewChainMaker ํ•จ์ˆ˜๋Š” ChainMaker์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
func NewChainMaker() *ChainMaker {
	cm := &ChainMaker{
		Description: "ChainMaker service",
		Iterate:     100000,
	}

	// ํ™˜๊ฒฝ์ด "process"์ธ ๊ฒฝ์šฐ, ์ฒด์ธ ๋ฉ”์ด์ปค ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ด๋ฏธ ์‹คํ–‰ ์ค‘์ธ์ง€ ํ™•์ธํ•˜๊ณ  ์‹คํ–‰ ์ค‘์ด๋ฉด ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
	if config.Environment == "process" {
		if processmanager.IsRunning(processmanager.CHAIN_MAKER) {
			log.Println("The chain maker process is already running.")
			os.Exit(1)
		}
		processmanager.Save(processmanager.CHAIN_MAKER)
	}

	// Pool ํด๋ผ์ด์–ธํŠธ๋ฅผ "rewind" ๋ชจ๋“œ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
	poolclient.Instance().SetMode("rewind")

	return cm
}

// Main ํ•จ์ˆ˜๋Š” ChainMaker์˜ ๋ฉ”์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Main() {
	defer func() {
		if processmanager.PID(processmanager.CHAIN_MAKER) == os.Getpid() {
			log.Println("Chain maker process has been successfully removed.")
			processmanager.Delete(processmanager.CHAIN_MAKER)
		}
	}()

	cm.Init()
	for i := 0; i < cm.Iterate; i++ {
		cm.Operation()
		time.Sleep(1 * time.Second) // ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์กฐ์ •
	}
}

// Init ํ•จ์ˆ˜๋Š” ChainMaker๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Init() {
	log.Println("The chain maker process has started.")
}

// Operation ํ•จ์ˆ˜๋Š” ChainMaker์˜ ์ฃผ์š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Operation() {
	lastBlock := mainchain.Instance().LastBlock()
	cm.Refresh(lastBlock)

	var errMsg string
	sync := cm.Sync(lastBlock, &errMsg)
	if !sync {
		cm.Consensus(lastBlock, &errMsg)
	}
}

// Refresh ํ•จ์ˆ˜๋Š” ์ตœ์‹  ๋ธ”๋ก์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Refresh(lastBlock *model.MainBlock) {
	bunch.Clean(lastBlock)

	info := bunch.InfoTxs()
	count := info["count"].(int)
	size := info["size"].(int)

	countLimit := config.BLOCK_TX_COUNT_LIMIT * 2
	sizeLimit := config.BLOCK_TX_SIZE_LIMIT * 2

	if count > countLimit || size > sizeLimit {
		poolclient.Instance().FlushTxs()
	}
}

// Sync ํ•จ์ˆ˜๋Š” ๋ธ”๋ก ๋™๊ธฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Sync(lastBlock *model.MainBlock, errMsg *string) bool {
	peers := tracker.GetPeers()
	hosts := cm.Hosts(peers)
	syncHosts := cm.SyncHosts(hosts)

	hosts = syncHosts["hosts"].([]string)
	longest := syncHosts["longest"].(map[string]interface{})

	decisions := observer.Instance().SeeBlocks(hosts, lastBlock.Height+1)

	var pull bool
	for _, decision := range decisions {
		if !pull {
			blocks := decision["blocks"].([]interface{})
			isLongest := decision["host"] == longest["host"]

			if cm.Pull(blocks, isLongest, errMsg) {
				pull = true
			}
		}
	}

	return pull
}

// Hosts ํ•จ์ˆ˜๋Š” ํ”ผ์–ด ๋ชฉ๋ก์—์„œ ํ˜ธ์ŠคํŠธ ๋ชฉ๋ก์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Hosts(peers []map[string]interface{}) []string {
	hosts := make([]string, len(peers))
	for i, peer := range peers {
		hosts[i] = peer["host"].(string)
	}
	return hosts
}

// SyncHosts ํ•จ์ˆ˜๋Š” ๋™๊ธฐํ™”ํ•  ํ˜ธ์ŠคํŠธ๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) SyncHosts(hosts []string) map[string]interface{} {
	height := max(chain.FixedHeight(), 1)
	rounds := observer.Instance().SeeMiningRounds(hosts, height)

	var longest map[string]interface{}
	var maxSyncLimit int
	var maxHash string
	var syncHosts []string

	for hash, cases := range rounds {
		for _, round := range cases {
			syncLimit := round["sync_limit"].(int)
			if syncLimit > maxSyncLimit {
				longest = round
				maxSyncLimit = syncLimit
				maxHash = hash
			}
		}
	}

	if longestHost, ok := longest["host"]; ok {
		syncHosts = append(syncHosts, longestHost.(string))
	}

	for hash, cases := range rounds {
		sort.SliceStable(cases, func(i, j int) bool {
			if cases[i]["sync_limit"].(int) == cases[j]["sync_limit"].(int) {
				return cases[i]["exec_time"].(int) < cases[j]["exec_time"].(int)
			}
			return cases[i]["sync_limit"].(int) > cases[j]["sync_limit"].(int)
		})

		syncHosts = append(syncHosts, cases[0]["host"].(string))
		if len(cases) > 1 {
			if hash == maxHash {
				for i := 0; i < 3; i++ {
					j := randInt(1, len(cases)-1)
					syncHosts = append(syncHosts, cases[j]["host"].(string))
				}
			} else {
				j := randInt(1, len(cases)-1)
				syncHosts = append(syncHosts, cases[j]["host"].(string))
			}
		}
	}

	return map[string]interface{}{
		"longest": longest,
		"hosts":   uniqueStrings(syncHosts),
	}
}

// Pull ํ•จ์ˆ˜๋Š” ๋ธ”๋ก ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Pull(blocks []interface{}, isLongest bool, errMsg *string) bool {
	var result bool
	for _, blockData := range blocks {
		block := model.NewMainBlock(blockData.(map[string]interface{}))
		if !cm.Append(block, isLongest, errMsg) {
			break
		}
		result = true
	}
	return result
}

// Append ํ•จ์ˆ˜๋Š” ๋ธ”๋ก์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Append(block *model.MainBlock, isLongest bool, errMsg *string) bool {
	machine := machine.Instance()
	lastBlock := mainchain.Instance().LastBlock()
	confirmedHeight := chain.ConfirmedHeight(block.STimestamp)
	confirmedHeight = hardfork.ConfirmedHeight(block, confirmedHeight)
	validators := chain.SelectValidators(confirmedHeight)

	if block.Height != lastBlock.Height+1 || len(validators) == 0 {
		*errMsg = "Waiting for resource blocks.. "
		poolclient.Instance().SetPolicy("main_chain_waiting", false)
		return true
	}

	block.Validators = validators
	machine.Init(lastBlock, block.STimestamp)
	machine.SetTransactions(block.Transactions)
	machine.PreLoad(block.UniversalUpdates, block.LocalUpdates)
	machine.PreCommit(confirmedHeight, errMsg)

	expectedBlock := machine.ExpectedBlock()

	validity := block.Validity() && (hardfork.MainCondition(block) || len(machine.Transactions) > 0) &&
		block.Blockhash == expectedBlock.Blockhash
	continuity := block.Height == lastBlock.Height+1 &&
		block.PreviousBlockhash == lastBlock.Blockhash &&
		block.STimestamp >= lastBlock.STimestamp+config.MAIN_CHAIN_INTERVAL

	if validity && continuity {
		poolclient.Instance().SetPolicy("main_chain_waiting", false)
		machine.Commit(block)
		return true
	} else if !continuity && isLongest {
		*errMsg = "Main chain fork."
		log.Println(*errMsg)
		poolclient.Instance().SetPolicy("main_chain_waiting", true)
		return false
	}

	*errMsg = "Blockhash is different."
	log.Println(*errMsg)
	return false
}

// Consensus ํ•จ์ˆ˜๋Š” ํ•ฉ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Consensus(lastBlock *model.MainBlock, errMsg *string) {
	roundKey := lastBlock.Blockhash
	chunks := bunch.ListChunk(roundKey)

	now := clock.UFloorTime()
	min := max(now-config.REFRESH_INTERVAL, lastBlock.STimestamp)
	roundTimestamp := now
	refresh := false

	if len(chunks) > 0 {
		roundTimestamp = maxInt(chunksTimes(chunks))
		if roundTimestamp < min {
			refresh = true
			roundTimestamp = now
		}
	}

	transactions := bunch.ListTx(roundTimestamp)
	if len(transactions) == 0 {
		return
	}

	confirmedHeight := chain.ConfirmedHeight(roundTimestamp)
	validators := chain.SelectValidators(confirmedHeight)
	main := validators[8]
	address := env.Node().Address()

	if !contains(validators, address) || lastBlock.STimestamp >= roundTimestamp {
		return
	}

	validChunks := filterChunks(chunks, validators)
	machine := machine.Instance()

	machine.Init(lastBlock, roundTimestamp)
	machine.SetTransactions(transactions)
	machine.PreCommit(confirmedHeight, errMsg)

	chunk := machine.Chunk()
	chunkHashes := getChunkHashes(validChunks)
	chunkHashes[address] = chunk.ChunkHash

	hypothesis := machine.Hypothesis(chunkHashes)
	expectedBlock := machine.ExpectedBlock()
	expectedBlock.Validators = validators

	if !hardfork.MainCondition(expectedBlock) && machine.TransactionCount() == 0 {
		return
	}

	if refresh || !chunkExists(validChunks, address) {
		bunch.AddChunk(chunk)
	}

	if address == main {
		bunch.AddHypothesis(hypothesis)
		hypotheses := bunch.ListHypothesis(lastBlock.Blockhash)
		cm.commitExpectedBlock(expectedBlock, hypotheses, lastBlock, validators)
	} else {
		if !cm.Votes(validators, getChunkKeys(validChunks)) {
			return
		}
		bunch.AddHypothesis(hypothesis)
		hypotheses := bunch.ListHypothesis(lastBlock.Blockhash)
		if !cm.Votes(validators, getHypothesisKeys(hypotheses)) {
			return
		}
	}
}

// commitExpectedBlock ํ•จ์ˆ˜๋Š” ์˜ˆ์ƒ ๋ธ”๋ก์„ ์ปค๋ฐ‹ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) commitExpectedBlock(expectedBlock *model.MainBlock, hypotheses []map[string]interface{}, lastBlock *model.MainBlock, validators []string) {
	expectedBlock = cm.Seal(expectedBlock, hypotheses)
	validity := expectedBlock.Validity() &&
		expectedBlock.Height == lastBlock.Height+1 &&
		(hardfork.MainCondition(expectedBlock) || len(expectedBlock.Transactions) > 0)

	continuity := expectedBlock.PreviousBlockhash == lastBlock.Blockhash &&
		expectedBlock.STimestamp >= lastBlock.STimestamp+config.MAIN_CHAIN_INTERVAL

	if validity && continuity {
		machine.Instance().Commit(expectedBlock)
	}
}

// Seal ํ•จ์ˆ˜๋Š” ์˜ˆ์ƒ ๋ธ”๋ก์„ ๋ด‰์ธํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Seal(expectedBlock *model.MainBlock, hypotheses []map[string]interface{}) *model.MainBlock {
	sealedSTimestamp := maxInt(hypothesesTimes(hypotheses))
	sealedHypotheses := sealHypotheses(hypotheses)

	expectedBlock.Seal = sealedHypotheses
	expectedBlock.STimestamp = sealedSTimestamp

	return expectedBlock
}

// Votes ํ•จ์ˆ˜๋Š” ํˆฌํ‘œ๋ฅผ ์ง‘๊ณ„ํ•ฉ๋‹ˆ๋‹ค.
func (cm *ChainMaker) Votes(validators, datas []string) bool {
	quorum := len(validators) * config.MAIN_CONSENSUS_PER
	votesCast := countVotes(validators, datas)

	return votesCast > quorum
}

// ๋ถ€๊ฐ€ ํ•จ์ˆ˜๋“ค

// contains ํ•จ์ˆ˜๋Š” ์Šฌ๋ผ์ด์Šค์— ํ•ญ๋ชฉ์ด ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

// uniqueStrings ํ•จ์ˆ˜๋Š” ์Šฌ๋ผ์ด์Šค์—์„œ ๊ณ ์œ ํ•œ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func uniqueStrings(input []string) []string {
	unique := make(map[string]struct{}, len(input))
	for _, str := range input {
		unique[str] = struct{}{}
	}

	output := make([]string, 0, len(unique))
	for str := range unique {
		output = append(output, str)
	}

	return output
}

// max ํ•จ์ˆ˜๋Š” ๋‘ ์ •์ˆ˜ ์ค‘ ๋” ํฐ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// maxInt ํ•จ์ˆ˜๋Š” ์ •์ˆ˜ ์Šฌ๋ผ์ด์Šค์—์„œ ๊ฐ€์žฅ ํฐ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func maxInt(ints []int) int {
	if len(ints) == 0 {
		return 0
	}
	max := ints[0]
	for _, v := range ints {
		if v > max {
			max = v
		}
	}
	return max
}

// randInt ํ•จ์ˆ˜๋Š” ์ฃผ์–ด์ง„ ๋ฒ”์œ„ ๋‚ด์—์„œ ๋ฌด์ž‘์œ„ ์ •์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func randInt(min, max int) int {
	return min + int(rand.Int31n(int32(max-min+1)))
}

// chunksTimes ํ•จ์ˆ˜๋Š” ์ฒญํฌ์˜ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func chunksTimes(chunks []map[string]interface{}) []int {
	times := make([]int, len(chunks))
	for i, chunk := range chunks {
		times[i] = chunk["s_timestamp"].(int)
	}
	return times
}

// getChunkHashes ํ•จ์ˆ˜๋Š” ์ฒญํฌ์˜ ํ•ด์‹œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func getChunkHashes(chunks map[string]map[string]interface{}) map[string]string {
	hashes := make(map[string]string, len(chunks))
	for key, chunk := range chunks {
		hashes[key] = chunk["chunk_hash"].(string)
	}
	return hashes
}

// getChunkKeys ํ•จ์ˆ˜๋Š” ์ฒญํฌ์˜ ํ‚ค๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func getChunkKeys(chunks map[string]map[string]interface{}) []string {
	keys := make([]string, 0, len(chunks))
	for key := range chunks {
		keys = append(keys, key)
	}
	return keys
}

// chunkExists ํ•จ์ˆ˜๋Š” ์ฒญํฌ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
func chunkExists(chunks map[string]map[string]interface{}, address string) bool {
	_, exists := chunks[address]
	return exists
}

// filterChunks ํ•จ์ˆ˜๋Š” ๊ฒ€์ฆ์ž ๋ชฉ๋ก์— ๋”ฐ๋ผ ์œ ํšจํ•œ ์ฒญํฌ๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
func filterChunks(chunks []map[string]interface{}, validators []string) map[string]map[string]interface{} {
	validChunks := make(map[string]map[string]interface{})
	for _, chunk := range chunks {
		signer := chunk["signer"].(string)
		if contains(validators, signer) {
			validChunks[signer] = chunk
		}
	}
	return validChunks
}

// hypothesesTimes ํ•จ์ˆ˜๋Š” ๊ฐ€์„ค์˜ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func hypothesesTimes(hypotheses []map[string]interface{}) []int {
	times := make([]int, len(hypotheses))
	for i, hypothesis := range hypotheses {
		times[i] = hypothesis["s_timestamp"].(int)
	}
	return times
}

// getHypothesisKeys ํ•จ์ˆ˜๋Š” ๊ฐ€์„ค์˜ ํ‚ค๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func getHypothesisKeys(hypotheses map[string]map[string]interface{}) []string {
	keys := make([]string, 0, len(hypotheses))
	for key := range hypotheses {
		keys = append(keys, key)
	}
	return keys
}

// sealHypotheses ํ•จ์ˆ˜๋Š” ๊ฐ€์„ค์„ ๋ด‰์ธํ•ฉ๋‹ˆ๋‹ค.
func sealHypotheses(hypotheses []map[string]interface{}) []map[string]interface{} {
	sealed := make([]map[string]interface{}, len(hypotheses))
	for i, hypothesis := range hypotheses {
		h := model.NewHypothesis(hypothesis)
		sealed[i] = h.Seal()
	}
	return sealed
}

// countVotes ํ•จ์ˆ˜๋Š” ํˆฌํ‘œ ์ˆ˜๋ฅผ ์ง‘๊ณ„ํ•ฉ๋‹ˆ๋‹ค.
func countVotes(validators, datas []string) int {
	votes := 0
	for _, validator := range validators {
		if contains(datas, validator) {
			votes++
		}
	}
	return votes
}

// main ํ•จ์ˆ˜๋Š” ChainMaker์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ฉ”์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
func main() {
	cm := NewChainMaker()
	cm.Main()
}

์ด ์ฝ”๋“œ๋Š” ChainMaker๋ผ๋Š” ๊ตฌ์กฐ์ฒด์™€ ๊ทธ์™€ ๊ด€๋ จ๋œ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜์—ฌ ๋ธ”๋ก์ฒด์ธ ๋„คํŠธ์›Œํฌ์—์„œ ์ฒด์ธ ์ƒ์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ํ•จ์ˆ˜์™€ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ํ•˜๋‚˜ํ•˜๋‚˜ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ํŒจํ‚ค์ง€ ์„ ์–ธ ๋ฐ ์ž„ํฌํŠธ

  • package service: ์ด ํŒŒ์ผ์ด service ํŒจํ‚ค์ง€์— ์†ํ•จ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

  • import (...): ์ด ์Šคํฌ๋ฆฝํŠธ์—์„œ ์‚ฌ์šฉ๋  ์™ธ๋ถ€ ํŒจํ‚ค์ง€๋“ค์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

ChainMaker ๊ตฌ์กฐ์ฒด์™€ ์ƒ์„ฑ์ž ํ•จ์ˆ˜

  • type ChainMaker struct { ... }: ChainMaker ๊ตฌ์กฐ์ฒด๋Š” ์ฒด์ธ ์ƒ์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ์ž‘์—…์— ๋Œ€ํ•œ ์„ค๋ช…๊ณผ ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ๋‹ด๋Š” ํ•„๋“œ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

  • func NewChainMaker() *ChainMaker { ... }: NewChainMaker ํ•จ์ˆ˜๋Š” ChainMaker ๊ตฌ์กฐ์ฒด์˜ ์ƒˆ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ Description ํ•„๋“œ๋Š” "ChainMaker service"๋กœ ์„ค์ •๋˜๊ณ , Iterate๋Š” 100000์œผ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

Main ํ•จ์ˆ˜

  • func (cm *ChainMaker) Main() { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Main ๋ฉ”์„œ๋“œ๋Š” ์ฒด์ธ ์ƒ์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ์ฃผ์š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    • defer func() { ... }: Main ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ์‹คํ–‰๋  ์ฝ”๋“œ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ํ˜„์žฌ PID์™€ ์ผ์น˜ํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

    • cm.Init(): ์ฒด์ธ ๋ฉ”์ด์ปค ํ”„๋กœ์„ธ์Šค๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

    • for i := 0; i < cm.Iterate; i++ { ... }: ๋ฐ˜๋ณต ํšŸ์ˆ˜๋งŒํผ Operation ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

      • cm.Operation(): ์ฒด์ธ ์ƒ์„ฑ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

      • time.Sleep(1 * time.Second): 1์ดˆ ๋™์•ˆ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.

Init ํ•จ์ˆ˜

  • func (cm *ChainMaker) Init() { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Init ๋ฉ”์„œ๋“œ๋Š” ์ฒด์ธ ๋ฉ”์ด์ปค ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹œ์ž‘๋˜์—ˆ์Œ์„ ๋กœ๊ทธ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

Operation ํ•จ์ˆ˜

  • func (cm *ChainMaker) Operation() { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Operation ๋ฉ”์„œ๋“œ๋Š” ์ฒด์ธ ์ƒ์„ฑ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    • lastBlock := mainchain.Instance().LastBlock(): ๋งˆ์ง€๋ง‰ ๋ธ”๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • cm.Refresh(lastBlock): ๋งˆ์ง€๋ง‰ ๋ธ”๋ก์„ ๊ธฐ์ค€์œผ๋กœ ์ƒํƒœ๋ฅผ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.

    • var errMsg string: ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•  ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

    • sync := cm.Sync(lastBlock, &errMsg): ๋™๊ธฐํ™”๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ์„ฑ๊ณต ์—ฌ๋ถ€์— ๋”ฐ๋ผ sync๊ฐ€ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.

    • if !sync { cm.Consensus(lastBlock, &errMsg) }: ๋™๊ธฐํ™”๊ฐ€ ์‹คํŒจํ•˜๋ฉด ํ•ฉ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

Refresh ํ•จ์ˆ˜

  • func (cm *ChainMaker) Refresh(lastBlock *model.MainBlock) { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Refresh ๋ฉ”์„œ๋“œ๋Š” ๋งˆ์ง€๋ง‰ ๋ธ”๋ก์„ ๊ธฐ์ค€์œผ๋กœ ์ƒํƒœ๋ฅผ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.

    • bunch.Clean(lastBlock): ๋งˆ์ง€๋ง‰ ๋ธ”๋ก์„ ๊ธฐ์ค€์œผ๋กœ ์ฒญ์†Œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    • info := bunch.InfoTxs(): ํŠธ๋žœ์žญ์…˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • count := info["count"].(int): ํŠธ๋žœ์žญ์…˜ ์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • size := info["size"].(int): ํŠธ๋žœ์žญ์…˜ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • countLimit := config.BLOCK_TX_COUNT_LIMIT * 2: ํŠธ๋žœ์žญ์…˜ ์ˆ˜ ์ œํ•œ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • sizeLimit := config.BLOCK_TX_SIZE_LIMIT * 2: ํŠธ๋žœ์žญ์…˜ ํฌ๊ธฐ ์ œํ•œ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • if count > countLimit || size > sizeLimit { poolclient.Instance().FlushTxs() }: ์ œํ•œ์„ ์ดˆ๊ณผํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์„ ํ”Œ๋Ÿฌ์‹œํ•ฉ๋‹ˆ๋‹ค.

Sync ํ•จ์ˆ˜

  • func (cm *ChainMaker) Sync(lastBlock *model.MainBlock, errMsg *string) bool { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Sync ๋ฉ”์„œ๋“œ๋Š” ๋™๊ธฐํ™”๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

    • peers := tracker.GetPeers(): ํ”ผ์–ด ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • hosts := cm.Hosts(peers): ํ˜ธ์ŠคํŠธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • syncHosts := cm.SyncHosts(hosts): ๋™๊ธฐํ™”ํ•  ํ˜ธ์ŠคํŠธ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • hosts = syncHosts["hosts"].([]string): ๋™๊ธฐํ™”ํ•  ํ˜ธ์ŠคํŠธ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • longest := syncHosts["longest"].(map[string]interface{}): ๊ฐ€์žฅ ๊ธด ํ˜ธ์ŠคํŠธ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • decisions := observer.Instance().SeeBlocks(hosts, lastBlock.Height+1): ๋ธ”๋ก์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

    • var pull bool: ๋™๊ธฐํ™” ์—ฌ๋ถ€๋ฅผ ์ €์žฅํ•  ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

    • for _, decision := range decisions { ... }: ๊ฒฐ์ • ๋ชฉ๋ก์„ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

      • if !pull { ... }: ์•„์ง ๋™๊ธฐํ™”๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ.

        • blocks := decision["blocks"].([]interface{}): ๋ธ”๋ก ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

        • isLongest := decision["host"] == longest["host"]: ๊ฐ€์žฅ ๊ธด ํ˜ธ์ŠคํŠธ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

        • if cm.Pull(blocks, isLongest, errMsg) { pull = true }: ๋ธ”๋ก์„ ๊ฐ€์ ธ์˜ค๊ณ  ๋™๊ธฐํ™” ์—ฌ๋ถ€๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

    • return pull: ๋™๊ธฐํ™” ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Hosts ํ•จ์ˆ˜

  • func (cm *ChainMaker) Hosts(peers []map[string]interface{}) []string { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Hosts ๋ฉ”์„œ๋“œ๋Š” ํ”ผ์–ด ๋ชฉ๋ก์—์„œ ํ˜ธ์ŠคํŠธ ๋ชฉ๋ก์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.

    • hosts := make([]string, len(peers)): ํ˜ธ์ŠคํŠธ ๋ชฉ๋ก์„ ์ €์žฅํ•  ์Šฌ๋ผ์ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

    • for i, peer := range peers { ... }: ํ”ผ์–ด ๋ชฉ๋ก์„ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

      • hosts[i] = peer["host"].(string): ํ˜ธ์ŠคํŠธ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜์—ฌ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

    • return hosts: ํ˜ธ์ŠคํŠธ ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

SyncHosts ํ•จ์ˆ˜

  • func (cm *ChainMaker) SyncHosts(hosts []string) map[string]interface{} { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ SyncHosts ๋ฉ”์„œ๋“œ๋Š” ๋™๊ธฐํ™”ํ•  ํ˜ธ์ŠคํŠธ ์ •๋ณด๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

    • height := max(chain.FixedHeight(), 1): ํ˜„์žฌ ๊ณ ์ •๋œ ๋†’์ด๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์ตœ์†Œ ๊ฐ’์„ 1๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • rounds := observer.Instance().SeeMiningRounds(hosts, height): ํ˜ธ์ŠคํŠธ๋“ค์˜ ๋งˆ์ด๋‹ ๋ผ์šด๋“œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • var longest map[string]interface{}; var maxSyncLimit int; var maxHash string; var syncHosts []string: ๋‹ค์–‘ํ•œ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

    • for hash, cases := range rounds { ... }: ๋ผ์šด๋“œ ์ •๋ณด๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

      • for _, round := range cases { ... }: ๊ฐ ๋ผ์šด๋“œ์˜ ์ผ€์ด์Šค๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

        • syncLimit := round["sync_limit"].(int): ๋™๊ธฐํ™” ์ œํ•œ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

        • if syncLimit > maxSyncLimit { ... }: ์ตœ๋Œ€ ๋™๊ธฐํ™” ์ œํ•œ์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

    • if longestHost, ok := longest["host"]; ok { syncHosts = append(syncHosts, longestHost.(string)) }: ๊ฐ€์žฅ ๊ธด ํ˜ธ์ŠคํŠธ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    • for hash, cases := range rounds { ... }: ๋ผ์šด๋“œ ์ •๋ณด๋ฅผ ๋‹ค์‹œ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

      • sort.SliceStable(cases, func(i, j int) bool { ... }): ์ผ€์ด์Šค๋ฅผ ์ •๋ ฌํ•ฉ๋‹ˆ๋‹ค.

      • syncHosts = append(syncHosts, cases[0]["host"].(string)): ๋™๊ธฐํ™” ํ˜ธ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

      • if len(cases) > 1 { ... }: ์ผ€์ด์Šค๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ธ ๊ฒฝ์šฐ ์ถ”๊ฐ€๋กœ ๋™๊ธฐํ™” ํ˜ธ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    • return map[string]interface{}{ ... }: ๋™๊ธฐํ™”ํ•  ํ˜ธ์ŠคํŠธ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Pull ํ•จ์ˆ˜

  • func (cm *ChainMaker) Pull(blocks []interface{}, isLongest bool, errMsg *string) bool { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Pull ๋ฉ”์„œ๋“œ๋Š” ๋ธ”๋ก์„ ๊ฐ€์ ธ์™€์„œ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    • var result bool: ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

    • for _, blockData := range blocks { ... }: ๋ธ”๋ก ๋ฐ์ดํ„ฐ๋ฅผ ์ˆœํšŒํ•ฉ๋‹ˆ๋‹ค.

      • block := model.NewMainBlock(blockData.(map[string]interface{})): ๋ธ”๋ก ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ์šด ๋ธ”๋ก์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

      • if !cm.Append(block, isLongest, errMsg) { break }: ๋ธ”๋ก์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹คํŒจํ•˜๋ฉด ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค.

      • result = true: ๊ฒฐ๊ณผ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

    • return result: ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Append ํ•จ์ˆ˜

  • func (cm *ChainMaker) Append(block *model.MainBlock, isLongest bool, errMsg *string) bool { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Append ๋ฉ”์„œ๋“œ๋Š” ๋ธ”๋ก์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    • machine := machine.Instance(): ๋จธ์‹  ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • lastBlock := mainchain.Instance().LastBlock(): ๋งˆ์ง€๋ง‰ ๋ธ”๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • confirmedHeight := chain.ConfirmedHeight(block.STimestamp): ํ™•์ธ๋œ ๋†’์ด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • confirmedHeight = hardfork.ConfirmedHeight(block, confirmedHeight): ํ•˜๋“œํฌํฌ๋œ ๋†’์ด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • validators := chain.SelectValidators(confirmedHeight): ๊ฒ€์ฆ์ž ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • if block.Height != lastBlock.Height+1 || len(validators) == 0 { ... }: ๋ธ”๋ก ๋†’์ด ๋˜๋Š” ๊ฒ€์ฆ์ž๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ.

      • *errMsg = "Waiting for resource blocks.. ": ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

      • poolclient.Instance().SetPolicy("main_chain_waiting", false): ์ •์ฑ…์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

      • return true: true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    • block.Validators = validators: ๋ธ”๋ก์— ๊ฒ€์ฆ์ž๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • machine.Init(lastBlock, block.STimestamp): ๋จธ์‹ ์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

    • machine.SetTransactions(block.Transactions): ํŠธ๋žœ์žญ์…˜์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • machine.PreLoad(block.UniversalUpdates, block.LocalUpdates): ์—…๋ฐ์ดํŠธ๋ฅผ ์‚ฌ์ „ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

    • machine.PreCommit(confirmedHeight, errMsg): ์‚ฌ์ „ ์ปค๋ฐ‹์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    • expectedBlock := machine.ExpectedBlock(): ์˜ˆ์ƒ ๋ธ”๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • validity := block.Validity() && ...: ์œ ํšจ์„ฑ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

    • continuity := block.Height == lastBlock.Height+1 && ...: ์—ฐ์†์„ฑ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

    • if validity && continuity { ... }: ์œ ํšจ์„ฑ๊ณผ ์—ฐ์†์„ฑ์ด ์žˆ๋Š” ๊ฒฝ์šฐ.

      • poolclient.Instance().SetPolicy("main_chain_waiting", false): ์ •์ฑ…์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

      • machine.Commit(block): ๋ธ”๋ก์„ ์ปค๋ฐ‹ํ•ฉ๋‹ˆ๋‹ค.

      • return true: true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    • else if !continuity && isLongest { ... }: ์—ฐ์†์„ฑ์ด ์—†๊ณ  ๊ฐ€์žฅ ๊ธด ๊ฒฝ์šฐ.

      • *errMsg = "Main chain fork.": ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

      • log.Println(*errMsg): ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋กœ๊ทธ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

      • poolclient.Instance().SetPolicy("main_chain_waiting", true): ์ •์ฑ…์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

      • return false: false๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

    • *errMsg = "Blockhash is different.": ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • log.Println(*errMsg): ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋กœ๊ทธ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

    • return false: false๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Consensus ํ•จ์ˆ˜

  • func (cm *ChainMaker) Consensus(lastBlock *model.MainBlock, errMsg *string) { ... }: ChainMaker ๊ตฌ์กฐ์ฒด์˜ Consensus ๋ฉ”์„œ๋“œ๋Š” ํ•ฉ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    • roundKey := lastBlock.Blockhash: ๋ผ์šด๋“œ ํ‚ค๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • chunks := bunch.ListChunk(roundKey): ์ฒญํฌ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • now := clock.UFloorTime(): ํ˜„์žฌ ์‹œ๊ฐ„์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • min := max(now-config.REFRESH_INTERVAL, lastBlock.STimestamp): ์ตœ์†Œ ์‹œ๊ฐ„์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • roundTimestamp := now: ๋ผ์šด๋“œ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • refresh := false: ๋ฆฌํ”„๋ ˆ์‹œ ์—ฌ๋ถ€๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • if len(chunks) > 0 { ... }: ์ฒญํฌ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ.

      • roundTimestamp = maxInt(chunksTimes(chunks)): ์ตœ๋Œ€ ์ฒญํฌ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

      • if roundTimestamp < min { ... }: ๋ผ์šด๋“œ ํƒ€์ž„์Šคํƒฌํ”„๊ฐ€ ์ตœ์†Œ ์‹œ๊ฐ„๋ณด๋‹ค ์ž‘์€ ๊ฒฝ์šฐ.

        • refresh = true: ๋ฆฌํ”„๋ ˆ์‹œ ์—ฌ๋ถ€๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

        • roundTimestamp = now: ๋ผ์šด๋“œ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ํ˜„์žฌ ์‹œ๊ฐ„์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • transactions := bunch.ListTx(roundTimestamp): ํŠธ๋žœ์žญ์…˜ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • if len(transactions) == 0 { return }: ํŠธ๋žœ์žญ์…˜์ด ์—†๋Š” ๊ฒฝ์šฐ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

    • confirmedHeight := chain.ConfirmedHeight(roundTimestamp): ํ™•์ธ๋œ ๋†’์ด๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • validators := chain.SelectValidators(confirmedHeight): ๊ฒ€์ฆ์ž ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • main := validators[8]: ์ฃผ ๊ฒ€์ฆ์ž๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • address := env.Node().Address(): ๋…ธ๋“œ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • if !contains(validators, address) || lastBlock.STimestamp >= roundTimestamp { return }: ๊ฒ€์ฆ์ž ๋ชฉ๋ก์— ์—†๊ฑฐ๋‚˜ ๋ผ์šด๋“œ ํƒ€์ž„์Šคํƒฌํ”„๋ณด๋‹ค ํฐ ๊ฒฝ์šฐ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

    • validChunks := filterChunks(chunks, validators): ์œ ํšจํ•œ ์ฒญํฌ๋ฅผ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.

    • machine := machine.Instance(): ๋จธ์‹  ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • machine.Init(lastBlock, roundTimestamp): ๋จธ์‹ ์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

    • machine.SetTransactions(transactions): ํŠธ๋žœ์žญ์…˜์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • machine.PreCommit(confirmedHeight, errMsg): ์‚ฌ์ „ ์ปค๋ฐ‹์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    • chunk := machine.Chunk(): ์ฒญํฌ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • chunkHashes := getChunkHashes(validChunks): ์ฒญํฌ ํ•ด์‹œ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • chunkHashes[address] = chunk.ChunkHash: ์ฒญํฌ ํ•ด์‹œ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • hypothesis := machine.Hypothesis(chunkHashes): ๊ฐ€์„ค์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • expectedBlock := machine.ExpectedBlock(): ์˜ˆ์ƒ ๋ธ”๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

    • expectedBlock.Validators = validators: ์˜ˆ์ƒ ๋ธ”๋ก์˜ ๊ฒ€์ฆ์ž๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    • if !hardfork.MainCondition(expectedBlock) && machine.TransactionCount() == 0 { return }: ํ•˜๋“œํฌํฌ ์กฐ๊ฑด์ด ์—†๊ณ  ํŠธ๋žœ์žญ์…˜ ์ˆ˜๊ฐ€ 0์ธ ๊ฒฝ์šฐ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

    • if refresh || !chunkExists(validChunks, address) { bunch.AddChunk(chunk) }: ๋ฆฌํ”„๋ ˆ์‹œ ๋˜๋Š” ์ฒญํฌ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ์ฒญํฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

    • if address == main { ... }: ์ฃผ์†Œ๊ฐ€ ์ฃผ ๊ฒ€์ฆ์ž์ธ ๊ฒฝ์šฐ.

      • bunch.AddHypothesis(hypothesis): ๊ฐ€์„ค์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

      • hypotheses := bunch.ListHypothesis(lastBlock.Blockhash): ๊ฐ€์„ค ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

      • cm.commitExpectedBlock(expectedBlock, hypotheses, lastBlock, validators): ์˜ˆ์ƒ ๋ธ”๋ก์„ ์ปค๋ฐ‹ํ•ฉ๋‹ˆ๋‹ค.

    • else { ... }: ์ฃผ์†Œ๊ฐ€ ์ฃผ ๊ฒ€์ฆ์ž๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ.

      • if !cm.Votes(validators, getChunkKeys(validChunks)) { return }: ํˆฌํ‘œ๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

      • bunch.AddHypothesis(hypothesis): ๊ฐ€์„ค์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

      • hypotheses := bunch.ListHypothesis(lastBlock.Blockhash): ๊ฐ€์„ค ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

      • if !cm.Votes(validators, getHypothesisKeys(hypotheses)) { return }: ํˆฌํ‘œ๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

๋ถ€๊ฐ€ ํ•จ์ˆ˜๋“ค

  • commitExpectedBlock, Seal, Votes, contains, uniqueStrings, max, maxInt, randInt, chunksTimes, getChunkHashes, getChunkKeys, chunkExists, filterChunks, hypothesesTimes, getHypothesisKeys, sealHypotheses, countVotes ๋“ฑ์˜ ํ•จ์ˆ˜๋Š” ๊ฐ๊ธฐ ๋‹ค์–‘ํ•œ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๊ทธ ๊ตฌ์ฒด์ ์ธ ๋‚ด์šฉ์€ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค.

main ํ•จ์ˆ˜

  • func main() { ... }: ๋ฉ”์ธ ํ•จ์ˆ˜๋Š” ChainMaker์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Main ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ฒด์ธ ์ƒ์„ฑ ์ž‘์—…์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

์ด ์ฝ”๋“œ๋Š” ๋ธ”๋ก์ฒด์ธ ๋„คํŠธ์›Œํฌ์—์„œ ์ฒด์ธ ์ƒ์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ณต์žกํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๋™๊ธฐํ™”, ํ•ฉ์˜, ๋ธ”๋ก ์ถ”๊ฐ€ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Last updated