[譯]使用go語言實(shí)現(xiàn)自己的PoS區(qū)塊鏈[完結(jié)]

這篇文章的原文為英文,出自 Coral Health公司: Code your own Proof of Stake blockchain in Go!

如果對本教程有任何疑問,請加入我們的電報群Telegram

上一篇文章中,我們討論了PoW算法,并展示了如何編寫Pow區(qū)塊鏈的代碼。最為流行的2個加密貨幣,比特幣(Bitcoin)和Eth都是基于工作量證明算法的。

工作量證明(PoW)算法的缺點(diǎn)是什么呢?其中一個最主要的就是電力的消耗。為了獲得挖比特幣所需要的硬件力量,人們展開了一場規(guī)模越來越大的礦機(jī)競賽??纯聪聢D中這個瘋狂的挖礦裝置:

這消耗了大量的電力。比特幣挖礦甚至超過了159個國家所消耗的能源。這是非常不負(fù)責(zé)任的;同時,從技術(shù)的角度來看,工作量證明也有其他的缺點(diǎn)。隨著越來越多的人參與挖掘,一致性算法的難度需要增加,從而需要更多的散列運(yùn)算能力。這意味著產(chǎn)生區(qū)塊和交易需要更長的時間來處理,并需要更昂貴的挖礦。工作量證明是一個競賽。

有許多思想領(lǐng)袖試圖尋找工作量證明(PoW)算法的替代品。到目前為止,最有希望的是權(quán)益證明(PoS)。基于NXT和Neo的i權(quán)益證明,已經(jīng)準(zhǔn)備好了生產(chǎn)鏈。Ethereum也很可能是為了證明他們的Casper項(xiàng)目已經(jīng)在他們的測試網(wǎng)絡(luò)上存在了。

那么到底什么是權(quán)益證明(PoS)呢?

用基于每個節(jié)點(diǎn)愿意作為抵押物的Token(令牌)數(shù)量,而不是節(jié)點(diǎn)相互競爭來獲得記賬權(quán)益。在Proof Stake中,塊是“minted”或“forged”(不存在“挖掘”,所以我們在Proof Stake中不使用該詞)。在本教程中,我們將交替使用術(shù)語“節(jié)點(diǎn)”和“驗(yàn)證器”。令牌是特定于鏈鏈的。所以在Ethereum,每個節(jié)點(diǎn)(驗(yàn)證器)都會把以太作為抵押物。

每個驗(yàn)證者愿意作為抵押品提供的令牌越多,他們就越有機(jī)會創(chuàng)造下一個塊并獲得獎勵。你可以認(rèn)為這是存款利息。你可以認(rèn)為這是存款利息。

類似地,你創(chuàng)造下一個塊的概率增加了你作為抵押的令牌。你正在“下注”你的令牌,這就是為什么這種共識機(jī)制被稱為利害關(guān)系的證據(jù)。

權(quán)益證明的缺點(diǎn)是什么呢?

您可能已經(jīng)猜到,一個擁有大量標(biāo)記的驗(yàn)證器將享有不成比例的高概率制造新的區(qū)塊。然而,這與我們在工作證明中所看到的并不完全不同。比特幣礦場變得如此強(qiáng)大,以至于普通人多年來無法在自己的筆記本電腦上開采。因此,許多人認(rèn)為“賭注證明”實(shí)際上更加民主化,因?yàn)槿魏稳酥辽俣伎梢栽谧约旱墓P記本電腦上參與進(jìn)來,而無需建立巨大的采礦平臺。他們不需要昂貴的硬件,只需要足夠的令牌就能獲利。

從技術(shù)和經(jīng)濟(jì)的角度來看,權(quán)益證明有其他不利之處。在這里,我們就不繼續(xù)深入了,但會有一個很好的介紹。在現(xiàn)實(shí)中,權(quán)益證明和工作量證明都有自己的優(yōu)勢,像Ethereum's Casper這樣的項(xiàng)目融合了兩者的特點(diǎn)。

像往常一樣,來理解權(quán)益證明是如何工作的最好方法就是寫自己的代碼!

讓我們來編碼一個基于權(quán)益證明的區(qū)塊鏈! 我們建議在開始之前,先查看下我們之前的文章。當(dāng)然,這不是必須的,但在下面的教程的某些部分,我們將快速過一下,所以它將幫助您審查它。

事先聲明

我們的blockchain將實(shí)現(xiàn)PoS的核心概念。然而,由于我們需要合理地設(shè)置文章長度,所以下面將省略PoS區(qū)塊鏈的生產(chǎn)級元素。如果你想在未來看到這些,請務(wù)必在我們的Telegram中告訴我們!

  • 完全對等實(shí)現(xiàn)。網(wǎng)絡(luò)是模擬的,中央塊鏈狀態(tài)是由一個單一的GO TCP服務(wù)器保持。在本教程中,狀態(tài)從單個服務(wù)器廣播到每個節(jié)點(diǎn)。

  • 錢包和余額追蹤。我們沒有在這個代碼中實(shí)現(xiàn)一個錢包。節(jié)點(diǎn)在網(wǎng)絡(luò)中被轉(zhuǎn)出去,令牌數(shù)量從標(biāo)準(zhǔn)輸入輸出接口(STDIN)中輸入。所以你可以輸入任何你想要的數(shù)量。一個完整的實(shí)現(xiàn)將每個節(jié)點(diǎn)與散列地址相關(guān)聯(lián),并跟蹤每個節(jié)點(diǎn)的令牌余額。

架構(gòu)

  • 我們將有一個基于GO的TCP服務(wù)器,其他節(jié)點(diǎn)(驗(yàn)證器)可以連接它。

  • 最新的區(qū)塊鏈狀態(tài)將周期性地廣播到每個節(jié)點(diǎn)。

  • 每個節(jié)點(diǎn)將提出新的塊。

  • 基于每個節(jié)點(diǎn)所持有的令牌數(shù)量,將隨機(jī)選擇一個節(jié)點(diǎn)(根據(jù)所持有的令牌數(shù)量加權(quán))作為獲勝者,并將其塊添加到塊鏈。

設(shè)置和導(dǎo)入

在開始編寫代碼之前,我們需要設(shè)置一個環(huán)境變量,以便TCP服務(wù)器知道要使用哪個端口。讓我們在工作目錄中創(chuàng)建一個.env文件,其中有一行:

ADDR=9000

我們的GO程序?qū)倪@個文件中讀取并知道暴露端口9000,這樣我們的節(jié)點(diǎn)可以連接到它。

現(xiàn)在讓我們在工作目錄中創(chuàng)建main.go文件并開始編碼!

像往常一樣,讓我們寫下我們的包聲明和需要導(dǎo)入的包。

package main

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

    "github.com/davecgh/go-spew/spew"
    "github.com/joho/godotenv"
)
  • Spew是一個很方便的包,將我們的區(qū)塊鏈優(yōu)雅的輸出到終端。

  • godotenv讓我們能夠從前面創(chuàng)建的.env文件中讀取配置。

快速脈沖檢測

如果你看過我們的其他教程,你會知道在這個階段,我們將要介紹我們的脈沖。我們是一家醫(yī)療保健公司,所以當(dāng)我們把數(shù)據(jù)添加到我們的區(qū)塊時,不會選擇像比特幣一樣無意義的內(nèi)容。把兩個手指放在手腕上,數(shù)一下你的脈搏數(shù)。這就是您的BPM數(shù),我們將在整個教程中使用。

全局變量

現(xiàn)在,我們聲明一下將會使用到的所有全局變量。

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

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

// candidateBlocks handles incoming blocks for validation
var candidateBlocks = make(chan Block)

// announcements broadcasts winning validator to all nodes
var announcements = make(chan string)

var mutex = &sync.Mutex{}

// validators keeps track of open validators and balances
var validators = make(map[string]int)
  • Block是每個區(qū)塊的內(nèi)容

  • Blockchain是我們的正式的區(qū)塊鏈,這只是一系列經(jīng)過驗(yàn)證的塊。將每個塊中的PrevHash與前一個塊的散列進(jìn)行比較,以確保我們的鏈?zhǔn)墙训摹?code>tempBlocks僅僅是一個區(qū)塊的存儲箱,然后在其中一個被選為獲勝者將被添加到Blockchain。

  • candidateBlocks是一個塊的通道;每個節(jié)點(diǎn)提出一個新的塊將它發(fā)送到這個通道。

  • announcements是一個通道,在這里,我們主要的TCP服務(wù)器向所有節(jié)點(diǎn)廣播最新的BooStand鏈。

  • mutex是一個標(biāo)準(zhǔn)變量,允許我們控制讀/寫和防止數(shù)據(jù)競爭。

  • validators是節(jié)點(diǎn)的映射和它們所持有的令牌的數(shù)量。

基本區(qū)塊鏈函數(shù)

在進(jìn)行權(quán)益證明算法的證明之前,讓我們寫出我們的標(biāo)準(zhǔn)區(qū)塊鏈函數(shù)函數(shù)。如果你看過我們以前的教程,應(yīng)該復(fù)習(xí)一下。如果你沒有,沒關(guān)系,但是我們會很快完成這個任務(wù)。

// SHA256 hasing
// calculateHash is a simple SHA256 hashing function
func calculateHash(s string) string {
    h := sha256.New()
    h.Write([]byte(s))
    hashed := h.Sum(nil)
    return hex.EncodeToString(hashed)
}

//calculateBlockHash returns the hash of all block information
func calculateBlockHash(block Block) string {
    record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash
    return calculateHash(record)
}

我們從散列函數(shù)開始。calculateHash獲取一個字符串并返回其Sh256哈希表示。calculateHash通過連接所有字段來散列塊的內(nèi)容。

// generateBlock creates a new block using previous block's hash
func generateBlock(oldBlock Block, BPM int, address string) (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 = calculateBlockHash(newBlock)
    newBlock.Validator = address

    return newBlock, nil
}

generateBlock是如何創(chuàng)建一個新的塊。我們在每個新塊中包括的重要字段是它的哈希簽名(以前通過calculateBlockHash計(jì)算)和前一個塊PrevHash的哈希(因此我們可以保持鏈的完整性)。我們還添加了一個Validator字段,這樣我們就知道了偽造塊的獲勝節(jié)點(diǎn)。

// isBlockValid makes 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 calculateBlockHash(newBlock) != newBlock.Hash {
        return false
    }

    return true
}

執(zhí)行和檢查的isBlockValid哈希鏈prevhash確保我們沒有被損壞。

節(jié)點(diǎn)(驗(yàn)證器)

當(dāng)一個驗(yàn)證器連接到我們的TCP服務(wù)器時,我們需要為它提供一些實(shí)現(xiàn)一些功能的功能:

  • 允許它輸入令牌余額(請記住本教程,由于沒有錢包邏輯,我們不執(zhí)行任何余額檢查)

  • 接收最新的鏈鏈廣播

  • 接收網(wǎng)絡(luò)中的驗(yàn)證程序獲得最新塊的廣播。

  • 將自己添加到驗(yàn)證器的整個列表中

  • 輸入塊數(shù)據(jù)BPM-記住,這是每個驗(yàn)證器的脈沖率

  • 提出一個新的區(qū)塊

我們將用手寫功能把這些都寫出來。在這里。別擔(dān)心!我們會陪你度過難關(guān)。

func handleConn(conn net.Conn) {
    defer conn.Close()

    go func() {
        for {
            msg := <-announcements
            io.WriteString(conn, msg)
        }
    }()
    // validator address
    var address string

    // allow user to allocate number of tokens to stake
    // the greater the number of tokens, the greater chance to forging a new block
    io.WriteString(conn, "Enter token balance:")
    scanBalance := bufio.NewScanner(conn)
    for scanBalance.Scan() {
        balance, err := strconv.Atoi(scanBalance.Text())
        if err != nil {
            log.Printf("%v not a number: %v", scanBalance.Text(), err)
            return
        }
        t := time.Now()
        address = calculateHash(t.String())
        validators[address] = balance
        fmt.Println(validators)
        break
    }

    io.WriteString(conn, "\nEnter a new BPM:")

    scanBPM := bufio.NewScanner(conn)

    go func() {
        for {
            // take in BPM from stdin and add it to blockchain after conducting necessary validation
            for scanBPM.Scan() {
                bpm, err := strconv.Atoi(scanBPM.Text())
                // if malicious party tries to mutate the chain with a bad input, delete them as a validator and they lose their staked tokens
                if err != nil {
                    log.Printf("%v not a number: %v", scanBPM.Text(), err)
                    delete(validators, address)
                    conn.Close()
                }

                mutex.Lock()
                oldLastIndex := Blockchain[len(Blockchain)-1]
                mutex.Unlock()

                // create newBlock for consideration to be forged
                newBlock, err := generateBlock(oldLastIndex, bpm, address)
                if err != nil {
                    log.Println(err)
                    continue
                }
                if isBlockValid(newBlock, oldLastIndex) {
                    candidateBlocks <- newBlock
                }
                io.WriteString(conn, "\nEnter a new BPM:")
            }
        }
    }()

    // simulate receiving broadcast
    for {
        time.Sleep(time.Minute)
        mutex.Lock()
        output, err := json.Marshal(Blockchain)
        mutex.Unlock()
        if err != nil {
            log.Fatal(err)
        }
        io.WriteString(conn, string(output)+"\n")
    }

}

以io.WriteString(conn,“Enter token.:”)開頭的部分允許驗(yàn)證器輸入他希望投資的令牌數(shù)量。然后,驗(yàn)證器被分配一個SHA256地址,該地址被添加到我們之前聲明的全局驗(yàn)證器映射中,以及我們新驗(yàn)證器的標(biāo)記數(shù)量。

以io.WriteString(conn,“Enter token.:”)開始的部分允許驗(yàn)證器輸入他希望投資的令牌數(shù)量。然后,驗(yàn)證器被分配一個SHA256地址,該地址被添加到我們之前聲明的全局驗(yàn)證器映射中,以及我們新驗(yàn)證器的標(biāo)記數(shù)量。

然后,我們進(jìn)入BPM,這是驗(yàn)證器的脈沖率,并創(chuàng)建一個單獨(dú)的GO協(xié)程來處理我們的塊邏輯。下面的一行很重要

delete(validators, address)

如果驗(yàn)證器試圖提出一個受污染的塊,在我們的例子中,一個不是整數(shù)的BPM,它拋出一個錯誤,我們立即從驗(yàn)證器列表中刪除驗(yàn)證器。他們不再有資格創(chuàng)造新的區(qū)塊并失去余額。

失去令牌余額的可能性是證明股權(quán)普遍安全的一個主要原因。如果你試圖為你的利益改變區(qū)塊,你會被抓住,你會失去所有的令牌余額股份。這是對壞演員的主要威懾。

然后,我們使用前面的generateBlock函數(shù)創(chuàng)建一個新塊,并將其發(fā)送到候選Blocks通道以進(jìn)行進(jìn)一步處理。將數(shù)據(jù)發(fā)送到信道使用此語法:

candidateBlocks <- newBlock

The last for loop periodically prints the latest blockchain so each validator knows the latest state. 最后一個循環(huán)周期性地打印最新的塊鏈,因此每個驗(yàn)證器都知道最新的狀態(tài)。

Picking a Winner

選舉勝出者

這就是權(quán)益證明的邏輯。我們需要寫下如何選擇獲勝的驗(yàn)證器;它們所賭的令牌數(shù)量越高,它們被選擇為偽造它們的塊的獲勝者的概率就越高。

為了簡化我們的代碼,我們只會讓驗(yàn)證者提出有資格被選為獲勝者的新塊。在傳統(tǒng)的權(quán)益證明中,驗(yàn)證者可以選擇為贏家,即使他們不提出新的塊。記住,權(quán)益證明不是一個定義,它是一個概念;權(quán)益證明有很多不同的實(shí)現(xiàn),就像工作量證明,每個實(shí)現(xiàn)都有自己的細(xì)微差別。

這是我們的pickWinner函數(shù)。我們將會和你一起完成它:

// pickWinner creates a lottery pool of validators and chooses the validator who gets to forge a block to the blockchain
// by random selecting from the pool, weighted by amount of tokens staked
func pickWinner() {
    time.Sleep(30 * time.Second)
    mutex.Lock()
    temp := tempBlocks
    mutex.Unlock()

    lotteryPool := []string{}
    if len(temp) > 0 {

        // slightly modified traditional proof of stake algorithm
        // from all validators who submitted a block, weight them by the number of staked tokens
        // in traditional proof of stake, validators can participate without submitting a block to be forged
    OUTER:
        for _, block := range temp {
            // if already in lottery pool, skip
            for _, node := range lotteryPool {
                if block.Validator == node {
                    continue OUTER
                }
            }

            // lock list of validators to prevent data race
            mutex.Lock()
            setValidators := validators
            mutex.Unlock()

            k, ok := setValidators[block.Validator]
            if ok {
                for i := 0; i < k; i++ {
                    lotteryPool = append(lotteryPool, block.Validator)
                }
            }
        }

        // randomly pick winner from lottery pool
        s := rand.NewSource(time.Now().Unix())
        r := rand.New(s)
        lotteryWinner := lotteryPool[r.Intn(len(lotteryPool))]

        // add block of winner to blockchain and let all the other nodes know
        for _, block := range temp {
            if block.Validator == lotteryWinner {
                mutex.Lock()
                Blockchain = append(Blockchain, block)
                mutex.Unlock()
                for _ = range validators {
                    announcements <- "\nwinning validator: " + lotteryWinner + "\n"
                }
                break
            }
        }
    }

    mutex.Lock()
    tempBlocks = []Block{}
    mutex.Unlock()
}

我們每30秒選取一個贏家,給每個驗(yàn)證者提出一個新的塊的時間。然后,我們需要創(chuàng)建一個lotteryPool,保存可以選擇為我們贏家的驗(yàn)證者的地址。然后,在繼續(xù)我們的邏輯之前,我們檢查一下在由if len(temp)>0提出的塊的臨時保持箱中是否實(shí)際提出了一些塊。

在OUTER for循環(huán)中,我們檢查以確保我們在臨時切片中沒有遇到相同的驗(yàn)證器。如果我們這樣做,跳過該塊并尋找下一個唯一的驗(yàn)證器。

在以k, ok := setValidators[block.Validator]開頭的小節(jié)中我們確保從臨時塊數(shù)據(jù)中獲得的驗(yàn)證器實(shí)際上是位于驗(yàn)證器映射中的合格驗(yàn)證器。如果它們存在,我們將它們添加到我們的lotteryPool池中。

我們?nèi)绾胃鶕?jù)他們所持有的令牌的數(shù)量分配適當(dāng)?shù)臋?quán)重?

我們把驗(yàn)證者地址的副本填在我們的lotteryPool上。他們?yōu)樗麄兯灥拿恳粋€令牌得到一份拷貝。因此,放置100個令牌的驗(yàn)證者將在lotteryPool中獲得100個條目。只放入1個令牌的驗(yàn)證器只能獲得1個條目。

我們隨機(jī)從我們的LoopyCype中挑選優(yōu)勝者,并把他們的地址分配給lotteryWinner。

然后,我們將它們的塊添加到我們的塊鏈中,并使用這種語法向其余節(jié)點(diǎn)通知獲勝者,該語法將消息發(fā)送到通知通道:

announcements <- "\nwinning validator: " + lotteryWinner + "\n"

我們清除了我們的防爆坦克,這樣就可以用下一組建議的塊再次填充。

這是權(quán)益證明一致性算法的核心!不算太差,不是嗎?

即將完成

現(xiàn)在我們完成main函數(shù),請看下面的代碼:

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

    // create genesis block
    t := time.Now()
    genesisBlock := Block{}
    genesisBlock = Block{0, t.String(), 0, calculateBlockHash(genesisBlock), "", ""}
    spew.Dump(genesisBlock)
    Blockchain = append(Blockchain, genesisBlock)

    // start TCP and serve TCP server
    server, err := net.Listen("tcp", ":"+os.Getenv("ADDR"))
    if err != nil {
        log.Fatal(err)
    }
    defer server.Close()

    go func() {
        for candidate := range candidateBlocks {
            mutex.Lock()
            tempBlocks = append(tempBlocks, candidate)
            mutex.Unlock()
        }
    }()

    go func() {
        for {
            pickWinner()
        }
    }()

    for {
        conn, err := server.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go handleConn(conn)
    }
}

我們從.env文件的內(nèi)容開始,這只是我們?yōu)門CP服務(wù)器使用的端口號。然后,我們創(chuàng)建一個genesisBlock創(chuàng)世區(qū)塊,新塊將被添加到它之后,以形成我們的區(qū)塊鏈。

我們啟動TCP服務(wù)器,并將.env文件中的端口暴露到新的驗(yàn)證器可以連接的端口中。

我們開啟一個Go協(xié)程,從candidateBlocks通道中取出區(qū)塊,并將它們填充到tempBlocks保持箱中,以便通過我們剛剛編寫的pickWinner函數(shù)進(jìn)行進(jìn)一步處理。然后我們?yōu)閜ickWinner函數(shù)啟動另一個GO協(xié)程。

最后一個for循環(huán)接受來自新的驗(yàn)證器的連接。

耶!我們完成了!

你在這里查看完整代碼:

mycoralhealth/blockchain-tutorial

What we just accomplished is pretty cool. We wrote up a robust Proof of Stake consensus algorithm from scratch and integrated it with actual TCP networking.

我們剛剛完成的事情很酷。我們從頭開始寫了一個健壯的權(quán)益證明一致性算法,并將其與實(shí)際的TCP網(wǎng)絡(luò)集成。

有趣的事情

讓我們試試看!打開一個終端窗口,啟動GO程序和TCP服務(wù)器,運(yùn)行go run main.go。正如我們預(yù)期的那樣,我們可以在控制臺上打印創(chuàng)世區(qū)塊(genesisBlock)。

現(xiàn)在讓我們啟動一個驗(yàn)證器。打開一個新的終端窗口并使用nc localhost 9000連接到我們的TCP服務(wù)器。

然后,我們提示添加一個令牌余額到股權(quán),輸入您希望驗(yàn)證器共享的令牌數(shù),然后輸入那個驗(yàn)證器的脈沖率。


因?yàn)槲覀兛梢杂泻芏囹?yàn)證器,讓我們用另一個終端窗口做同樣的事情。


當(dāng)你添加新的終端時,注意你的第一個終端。我們看到驗(yàn)證器得到指定的地址,并且每次添加新的驗(yàn)證碼時,我們都會收到一個驗(yàn)證器列表。


稍等一下,看看你的新終端。正在發(fā)生的是我們的項(xiàng)目花了一些時間來挑選一個勝利者。然后繁榮!獲勝者被選中!

在我們的例子中,選擇了第一個驗(yàn)證器(我們可以通過將驗(yàn)證器的地址與主終端中打印的驗(yàn)證器列表進(jìn)行比較來驗(yàn)證它)。

再等一會兒,咚!我們看到了我們的新的塊鏈廣播到所有的終端,我們的獲勝驗(yàn)證器的塊包含他的BPM在最新的塊中!酷,對吧?

下一步

你應(yīng)該為通過本教程而感到自豪。大多數(shù)區(qū)塊鏈愛好者和許多程序員都聽說過權(quán)益證明,但卻無法清楚的解釋它是什么。你已經(jīng)走得更遠(yuǎn),實(shí)際上從零開始建立了一個證明鏈鏈的證據(jù)!你更接近下一代區(qū)塊鏈技術(shù)的專家。

因?yàn)檫@是一個教程,我們可以做更多的事情來制作一個能用于生產(chǎn)環(huán)境的區(qū)塊鏈。接下來要探索的是:

  • 閱讀我們的工作證明教程并修補(bǔ)它,看看是否可以創(chuàng)建混合鏈鏈。

  • 添加時間塊,其中驗(yàn)證器有機(jī)會提出新的塊。我們的代碼版本允許驗(yàn)證器隨時提出新的塊,所以一些塊可以周期性地從考慮中被切斷。

  • 添加完整的對等能力。這基本上意味著每個驗(yàn)證器將運(yùn)行自己的TCP服務(wù)器以及連接到其他服務(wù)器。我們需要添加邏輯,以便每個節(jié)點(diǎn)可以找到彼此。在這里閱讀更多。

也看看我們的其他教程

希望你能喜歡這些教程。像往常一樣,請務(wù)必加入我們的電報群(Telegram)!它是咨詢問題并得到技術(shù)支持的最佳方式。當(dāng)然,它是免費(fèi)的!我們也會在那里提供更熱門的教程。

要了解更多關(guān)于Coral Health的信息,以及我們?nèi)绾问褂眠@個區(qū)塊鏈來推進(jìn)個性化醫(yī)學(xué)研究,請?jiān)L問我們的網(wǎng)站并在Twitter上關(guān)注我們。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容