在本篇博客中,我们将深入探讨一个用Go语言编写的简易区块链实现。通过这个示例,我们可以理解区块链背后的核心概念,包括如何创建和管理区块、如何处理交易以及如何通过工作量证明算法保证区块链的安全性。我们还会探讨如何使用BoltDB这个轻量级的键值数据库来持久化存储区块链数据。在这个过程中,我们将一步一步构建起区块链的基本结构,并演示如何通过命令行界面(CLI)与区块链进行交互。
建议观看顺序:
Go语言实现简单区块链-CSDN博客
Go语言实现简单区块链——增加POW机制-CSDN博客
为了能够在网络中传输或在磁盘上存储,我们需要将区块序列化和反序列化。序列化是将区块转换为字节序列的过程,而反序列化是将字节序列还原为原始区块的过程。
序列化 (Serialize
方法): 使用Go的encoding/gob
包来进行序列化操作。这个方法将Block
结构体编码成字节流,以便于存储或网络传输。
反序列化 (DeserializeBlock
函数): 与序列化相反,这个函数将字节流解码回Block
结构体。
新建区块 (NewBlock
函数): 当需要添加新的区块时,我们会调用这个函数。该函数接受交易列表和前一个区块的哈希值作为参数,创建一个新的区块。在这个过程中,我们还会进行工作量证明的计算。
创世区块 (NewGenesisBlock
函数): 创世区块是区块链中的第一个区块。这个函数通过调用NewBlock
函数并传递一个特殊的coinbase交易来创建创世区块。
// -*- coding: utf-8 -*- // Time : 2024/4/15 22:15 // Author : blue // File : block.go // Software: Goland package main import ( "bytes" "crypto/sha256" "encoding/gob" "log" "time" ) // Block keeps block headers type Block struct { Timestamp int64 // 时间戳 Transactions []*Transaction // 交易 PrevBlockHash []byte // 前一个区块的哈希 Hash []byte // 当前区块的哈希 Nonce int // 随机数 } // Serializes 是将区块序列化为字节数组 func (b *Block) Serialize() []byte { var result bytes.Buffer encoder := gob.NewEncoder(&result) err := encoder.Encode(b) if err != nil { log.Panic(err) } return result.Bytes() } // HashTransactions将区块中的所有交易序列化并返回一个哈希值 func (b *Block) HashTransactions() []byte { var txHashes [][]byte var txHash [32]byte // 遍历区块中的所有交易 for _, tx := range b.Transactions { txHashes = append(txHashes, tx.ID) } txHash = sha256.Sum256(bytes.Join(txHashes, []byte{})) return txHash[:] } // NewBlock 将创建一个新的区块 func NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block { // 创建区块 block := &Block{time.Now().Unix(), transactions, prevBlockHash, []byte{}, 0} // 创建工作量证明 pow := NewProofOfWork(block) // 运行工作量证明 nonce, hash := pow.Run() block.Hash = hash[:] block.Nonce = nonce return block } // NewGenesisBlock 将创建并返回创世区块 func NewGenesisBlock(coinbase *Transaction) *Block { return NewBlock([]*Transaction{coinbase}, []byte{}) } // DeserializeBlock 将区块的字节数组反序列化为区块 func DeserializeBlock(d []byte) *Block { var block Block decoder := gob.NewDecoder(bytes.NewReader(d)) err := decoder.Decode(&block) if err != nil { log.Panic(err) } return &block }
这段代码实现了区块链的核心数据结构和功能,包括区块链、区块迭代器以及创建、查询和添加区块等操作。让我们深入分析:
FindUnspentTransactions
获取未花费交易。// -*- coding: utf-8 -*- // Time : 2024/4/15 22:15 // Author : blue // File : blockchains.go // Software: Goland package main import ( "encoding/hex" "fmt" "github.com/boltdb/bolt" "log" "os" ) const dbFile = "blockchain.db" //数据库文件 const blocksBucket = "blocks" //存储区块的桶 const genesisCoinbaseData = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks" //创世块的交易数据 // Blockchain 包含一个区块链 type Blockchain struct { tip []byte //最新区块的哈希 db *bolt.DB //数据库指针 } // BlockchainIterator 将用于迭代区块链 type BlockchainIterator struct { currentHash []byte //当前区块的哈希 db *bolt.DB //数据库指针 } // MineBlock 将用于挖掘新块 func (bc *Blockchain) MineBlock(transactions []*Transaction) { var lastHash []byte //记录最新区块的哈希 //获取最新区块的哈希 err := bc.db.View(func(tx *bolt.Tx) error { //获取区块桶 b := tx.Bucket([]byte(blocksBucket)) //获取最新区块的哈希 lastHash = b.Get([]byte("l")) return nil }) if err != nil { log.Panic(err) } //创建新区块(包含验证) newBlock := NewBlock(transactions, lastHash) //将新区块存储到数据库中 err = bc.db.Update(func(tx *bolt.Tx) error { //获取区块桶 b := tx.Bucket([]byte(blocksBucket)) //将新区块存储到数据库中 err := b.Put(newBlock.Hash, newBlock.Serialize()) if err != nil { log.Panic(err) } //将最新区块的哈希存储到数据库中 err = b.Put([]byte("l"), newBlock.Hash) if err != nil { log.Panic(err) } //更新区块链的tip bc.tip = newBlock.Hash return nil }) } // FindUnspentTransactions 返回未花费的交易 func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction { //未花费的交易 var unspentTXs []Transaction //已花费的输出 spentTXOs := make(map[string][]int) //迭代区块链 bci := bc.Iterator() for { //获取下一个区块 block := bci.Next() //遍历区块中的交易 for _, tx := range block.Transactions { //将交易ID转换为字符串 txID := hex.EncodeToString(tx.ID) Outputs: //遍历交易中的输出 for outIdx, out := range tx.Vout { //检查输出是否已经被花费 if spentTXOs[txID] != nil { //遍历已花费的输出 for _, spentOut := range spentTXOs[txID] { //如果输出已经被花费,则跳过 if spentOut == outIdx { continue Outputs } } } //如果输出可以被解锁,则将交易添加到未花费的交易中 if out.CanBeUnlockedWith(address) { unspentTXs = append(unspentTXs, *tx) } } //如果交易不是coinbase交易,则遍历交易的输入 if tx.IsCoinbase() == false { //遍历交易的输入 for _, in := range tx.Vin { //如果输入可以解锁,则将输出添加到已花费的输出中 if in.CanUnlockOutputWith(address) { //将交易ID转换为字符串 inTxID := hex.EncodeToString(in.Txid) //将输出添加到已花费的输出中 spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout) } } } } //如果区块的前一个区块哈希为空,则退出循环 if len(block.PrevBlockHash) == 0 { break } } //返回未花费的交易 return unspentTXs } // FindUTXO 返回未花费的输出 func (bc *Blockchain) FindUTXO(address string) []TXOutput { var UTXOs []TXOutput //未花费的交易 unspentTransactions := bc.FindUnspentTransactions(address) //遍历未花费的交易 for _, tx := range unspentTransactions { //遍历交易的输出 for _, out := range tx.Vout { //如果输出可以被解锁,则将输出添加到未花费的输出中 if out.CanBeUnlockedWith(address) { UTXOs = append(UTXOs, out) } } } return UTXOs } // FindSpendableOutputs 返回足够的未花费输出以满足要求的金额 func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) { //未花费的输出 unspentOutputs := make(map[string][]int) //未花费的交易 unspentTXs := bc.FindUnspentTransactions(address) //累计金额 accumulated := 0 Work: //遍历未花费的交易 for _, tx := range unspentTXs { //将交易ID转换为字符串 txID := hex.EncodeToString(tx.ID) //遍历交易的输出 for outIdx, out := range tx.Vout { //如果输出可以被解锁且累计金额小于要求的金额,则将输出添加到未花费的输出中 if out.CanBeUnlockedWith(address) && accumulated < amount { //将输出添加到未花费的输出中 accumulated += out.Value //将输出的索引添加到未花费的输出中 unspentOutputs[txID] = append(unspentOutputs[txID], outIdx) //如果累计金额大于等于要求的金额,则退出循环 if accumulated >= amount { break Work } } } } //返回累计金额和未花费的输出 return accumulated, unspentOutputs } // Iterator 返回一个迭代器 func (bc *Blockchain) Iterator() *BlockchainIterator { //迭代器对象是一个指向区块链的指针和一个指向数据库的指针 bci := &BlockchainIterator{bc.tip, bc.db} return bci } // Next 返回区块链中的下一个区块 func (i *BlockchainIterator) Next() *Block { var block *Block //获取当前区块 err := i.db.View(func(tx *bolt.Tx) error { //获取区块桶 b := tx.Bucket([]byte(blocksBucket)) //获取当前区块 encodedBlock := b.Get(i.currentHash) //反序列化区块 block = DeserializeBlock(encodedBlock) return nil }) if err != nil { log.Panic(err) } //更新当前区块的哈希 i.currentHash = block.PrevBlockHash return block } // dbExists 检查数据库是否存在 func dbExists() bool { if _, err := os.Stat(dbFile); os.IsNotExist(err) { return false } return true } // NewBlockchain 创建一个新的区块 func NewBlockchain(address string) *Blockchain { //检查数据库是否存在 if dbExists() == false { fmt.Println("No existing blockchain found. Create one first.") os.Exit(1) } //存储最新区块的哈希 var tip []byte db, err := bolt.Open(dbFile, 0600, nil) if err != nil { log.Panic(err) } //更新数据库 err = db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(blocksBucket)) tip = b.Get([]byte("l")) return nil }) if err != nil { log.Panic(err) } //创建一个区块链对象 bc := Blockchain{tip, db} return &bc } // CreateBlockchain 创建一个新的区块链 func CreateBlockchain(address string) *Blockchain { //检查数据库是否存在 if dbExists() { fmt.Println("Blockchain already exists.") os.Exit(1) } var tip []byte //打开数据库 db, err := bolt.Open(dbFile, 0600, nil) if err != nil { log.Panic(err) } //更新数据库 err = db.Update(func(tx *bolt.Tx) error { //创建一个新的coinbase交易 cbtx := NewCoinbaseTX(address, genesisCoinbaseData) //创建一个新的区块 genesis := NewGenesisBlock(cbtx) //创建一个新的桶 b, err := tx.CreateBucket([]byte(blocksBucket)) if err != nil { log.Panic(err) } //将区块存储到数据库中 err = b.Put(genesis.Hash, genesis.Serialize()) if err != nil { log.Panic(err) } err = b.Put([]byte("l"), genesis.Hash) if err != nil { log.Panic(err) } tip = genesis.Hash return nil }) if err != nil { log.Panic(err) } bc := Blockchain{tip, db} return &bc } // AddBlock 将用于添加区块到区块链 func AddBlock(address string) { //创建一个新的区块 bc := NewBlockchain(address) defer bc.db.Close() //创建一个新的coinbase交易 //cbtx := NewCoinbaseTX(address, "") //挖掘新块并存放到数据库中 bc.MineBlock([]*Transaction{NewCoinbaseTX(address, "")}) //往address地址发送奖励 fmt.Println("Coinbase交易完成,以奖励的方式将硬币发送到地址:", address) fmt.Println("Success!") }
这段代码实现了区块链中的工作量证明 (Proof of Work, PoW) 机制,用于确保区块链的安全性,防止恶意攻击。让我们深入理解其原理和实现:
PoW 是一种共识机制,要求节点进行一定的计算工作才能将新区块添加到区块链中。这种机制通过消耗计算资源来增加攻击成本,从而保证区块链的安全性。
ProofOfWork 结构体:
block
: 指向区块的指针,包含区块头信息。target
: 指向一个大整数的指针,代表挖矿难度目标。哈希值必须小于该目标才算有效。NewProofOfWork:
target
值。targetBits
常量控制,值越小,难度越大。prepareData:
nonce
等。Run:
nonce
值,直到找到满足难度目标的哈希值。target
进行比较。target
,则挖矿成功,返回 nonce
和哈希值。nonce
值。Validate:
nonce
计算哈希值,并与 target
比较。target
,则验证通过,返回 true
。false
。// -*- coding: utf-8 -*- // Time : 2024/4/14 23:48 // Author : blue // File : proofwork.go // Software: Goland package main import ( "bytes" "crypto/sha256" "fmt" "math" "math/big" ) var ( maxNonce = math.MaxInt64 ) const targetBits = 16 // ProofOfWork 包含一个指向区块的指针和一个指向big.Int的指针 type ProofOfWork struct { block *Block target *big.Int } // NewProofOfWork 创建并返回一个ProofOfWork结构 func NewProofOfWork(b *Block) *ProofOfWork { target := big.NewInt(1) target.Lsh(target, uint(256-targetBits)) pow := &ProofOfWork{b, target} return pow } // prepareData 准备数据 func (pow *ProofOfWork) prepareData(nonce int) []byte { data := bytes.Join( [][]byte{ pow.block.PrevBlockHash, pow.block.HashTransactions(), IntToHex(pow.block.Timestamp), IntToHex(int64(targetBits)), IntToHex(int64(nonce)), }, []byte{}, ) return data } // Run 查找有效的哈希 func (pow *ProofOfWork) Run() (int, []byte) { var hashInt big.Int var hash [32]byte nonce := 0 fmt.Printf("Mining a new block") for nonce < maxNonce { data := pow.prepareData(nonce) hash = sha256.Sum256(data) fmt.Printf("\r%x", hash) hashInt.SetBytes(hash[:]) if hashInt.Cmp(pow.target) == -1 { break } else { nonce++ } } fmt.Print("\n\n") return nonce, hash[:] } // Validate 验证工作量证明 func (pow *ProofOfWork) Validate() bool { var hashInt big.Int data := pow.prepareData(pow.block.Nonce) hash := sha256.Sum256(data) hashInt.SetBytes(hash[:]) isValid := hashInt.Cmp(pow.target) == -1 return isValid }
这段代码定义了区块链中的交易数据结构,包括交易输入、交易输出和交易本身,以及创建 coinbase 交易和普通交易的函数。让我们深入分析:
在区块链技术,特别是比特币及其衍生加密货币中,交易的验证过程涉及到两个重要的概念:锁定脚本(ScriptPubKey)和解锁脚本(ScriptSig)。这两个脚本共同工作,确保只有资产的合法拥有者才能花费这些资产。在本文Transaction.go
代码示例中,为了便于理解和实现,锁定脚本和解锁脚本简化为货币持有者的地址。
// TXInput 表示交易输入 type TXInput struct { Txid []byte //引用的交易ID,表示交易的哈希 Vout int //引用的输出索引 ScriptSig string //解锁脚本 }
在TXOutput
结构体中,ScriptPubKey
字段代表锁定脚本。它的作用是指定谁有权利花费这个输出中的资金。在真实的比特币系统中,ScriptPubKey
可以包含更复杂的脚本,但在这个简化模型中,它被设置为接收方的地址。
// TXOutput 表示交易输出 type TXOutput struct { Value int // 输出金额 ScriptPubKey string //锁定脚本 }
与此同时,在TXInput
结构体中,ScriptSig
字段代表解锁脚本。当你尝试花费某个输出时,你需要提供一个ScriptSig
,这个脚本能够"解锁"那个输出中的ScriptPubKey
所施加的限制。
在真实的比特币系统中,解锁脚本通常包含与锁定脚本相匹配的公钥和一个签名,这个签名是使用与公钥相对应的私钥生成的。这样,只有拥有正确私钥的人才能生成有效的签名,从而成功解锁输出。
在本文代码示例中,锁定脚本和解锁脚本被简化为直接使用地址字符串。这意味着,如果一个输出的ScriptPubKey
与某个地址匹配,那么任何包含相同地址作为ScriptSig
的输入都能解锁并花费这个输出。
// CanUnlockOutputWith 将检查输出是否可以使用提供的数据解锁 func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool { return in.ScriptSig == unlockingData } // CanBeUnlockedWith 将检查输出是否可以使用提供的数据解锁 func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool { return out.ScriptPubKey == unlockingData }
这种简化的实现方式有助于理解区块链交易的基本原理,即资产的转移需要通过正确的解锁条件。然而,在实际的区块链应用中,这种机制要复杂得多,涉及到密钥对和加密签名,以确保交易的安全性和用户的隐私。
// -*- coding: utf-8 -*- // Time : 2024/4/15 22:16 // Author : blue // File : Transaction.go // Software: Goland package main import ( "bytes" "crypto/sha256" "encoding/gob" "encoding/hex" "fmt" "log" ) // 表示每个区块链的奖励 const subsidy = 10 // Transaction 表示一笔交易 type Transaction struct { ID []byte //交易ID,表示交易的哈希 Vin []TXInput //交易输入 Vout []TXOutput //交易输出 } // TXInput 表示交易输入 type TXInput struct { Txid []byte //引用的交易ID,表示交易的哈希 Vout int //引用的输出索引 ScriptSig string //解锁脚本 } // TXOutput 表示交易输出 type TXOutput struct { Value int // 输出金额 ScriptPubKey string //锁定脚本 } // IsCoinbase 将检查交易是否为coinbase交易 func (tx Transaction) IsCoinbase() bool { return len(tx.Vin) == 1 && len(tx.Vin[0].Txid) == 0 && tx.Vin[0].Vout == -1 } // SetID 将设置交易ID,交易ID是交易的哈希 func (tx *Transaction) SetID() { //创建一个缓冲区 var encoded bytes.Buffer //创建一个哈希 var hash [32]byte //创建一个编码器 enc := gob.NewEncoder(&encoded) //编码 err := enc.Encode(tx) if err != nil { log.Panic(err) } //计算哈希 hash = sha256.Sum256(encoded.Bytes()) //设置ID tx.ID = hash[:] } // CanUnlockOutputWith 将检查输出是否可以使用提供的数据解锁 func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool { return in.ScriptSig == unlockingData } // CanBeUnlockedWith 将检查输出是否可以使用提供的数据解锁 func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool { return out.ScriptPubKey == unlockingData } // NewCoinbaseTX 将创建一个新的coinbase交易 func NewCoinbaseTX(to, data string) *Transaction { // 如果没有数据,则使用默认数据 if data == "" { data = fmt.Sprintf("Reward to '%s'", to) } //coinbase交易没有输入,所以Txid为空,Vout为-1 txin := TXInput{[]byte{}, -1, data} //创建一个输出 txout := TXOutput{subsidy, to} //创建一个交易 tx := Transaction{nil, []TXInput{txin}, []TXOutput{txout}} tx.SetID() return &tx } // NewUTXOTransaction 将创建一个新的UTXO交易 func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction { var inputs []TXInput var outputs []TXOutput //获取未花费的输出 acc, validOutputs := bc.FindSpendableOutputs(from, amount) //检查余额是否足够 if acc < amount { log.Panic("ERROR: Not enough funds") } // 构建一个输入列表 for txid, outs := range validOutputs { //将交易ID转换为字节数组 txID, err := hex.DecodeString(txid) if err != nil { log.Panic(err) } //遍历输出 for _, out := range outs { //创建一个输入 input := TXInput{txID, out, from} //添加到输入列表 inputs = append(inputs, input) } } //创建一个输出 outputs = append(outputs, TXOutput{amount, to}) //如果余额大于转账金额,则创建一个找零 if acc > amount { outputs = append(outputs, TXOutput{acc - amount, from}) // a change } //创建一个交易 tx := Transaction{nil, inputs, outputs} tx.SetID() return &tx } // IntToHex 将整数转换为字节数组 func IntToHex(num int64) []byte { buff := new(bytes.Buffer) err := binary.Write(buff, binary.BigEndian, num) if err != nil { log.Panic(err) } return buff.Bytes() }
这段代码实现了一个简单的命令行界面 (CLI),用于与区块链进行交互,包括创建区块链、查询余额、打印区块链和发送交易等功能。让我们逐一解析:
CLI
结构体用于处理命令行参数和执行相应操作。// -*- coding: utf-8 -*- // Time : 2024/4/15 22:18 // Author : blue // File : cli.go // Software: Goland package main import ( "fmt" "os" "strconv" "time" ) // CLI 处理命令行参数 type CLI struct { } // createBlockchain 创建区块链 func (cli *CLI) createBlockchain(address string) { // 创建区块链 bc := CreateBlockchain(address) bc.db.Close() fmt.Println("Done!") } // getBalance 获取地址的余额 func (cli *CLI) getBalance(address string) { // 创建区块链 bc := NewBlockchain(address) defer bc.db.Close() balance := 0 // 获取地址的UTXO UTXOs := bc.FindUTXO(address) // 计算余额 for _, out := range UTXOs { balance += out.Value } fmt.Printf("Balance of '%s': %d\n", address, balance) } // printUsage 打印用法 func (cli *CLI) printUsage() { fmt.Println("Usage:") fmt.Println(" English: getbalance -address ADDRESS - Get balance of ADDRESS、、、中文:getbalance - address ADDRESS -获取地址的余额") fmt.Println(" createblockchain -address ADDRESS - Create a blockchain and send genesis block reward to ADDRESS") fmt.Println(" printchain - Print all the blocks of the blockchain") fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send AMOUNT of coins from FROM address to TO") } // validateArgs 验证参数 func (cli *CLI) validateArgs() { // 验证参数 if len(os.Args) < 2 { //什么意思:如果命令行参数少于2个 // 打印用法 cli.printUsage() os.Exit(1) //什么意思:退出 } } // printChain 打印区块链 func (cli *CLI) printChain() { // 创建区块链 bc := NewBlockchain("") defer bc.db.Close() // 创建迭代器 bci := bc.Iterator() // 循环打印区块链中的区块 for { block := bci.Next() fmt.Println("=========================================") //把时间戳转换为时间 timeFormat := time.Unix(block.Timestamp, 0) fmt.Println("timestamp:", timeFormat) fmt.Printf("PrevBlockHash: %x\n", block.PrevBlockHash) fmt.Printf("BlockHash: %x\n", block.Hash) //显示完整的交易信息 fmt.Println("Transactions:") for _, tx := range block.Transactions { //显示交易ID //fmt.Printf("Transaction ID: %x\n", tx.ID) var str string for _, value := range tx.ID { str += strconv.Itoa(int(value)) } fmt.Println("Transaction ID: " + str) fmt.Println("TXInput:") for _, tx := range tx.Vin { fmt.Println(tx.Txid) fmt.Println(tx.Vout) fmt.Println(tx.ScriptSig) } fmt.Println("TXOutputs:") for _, tx := range tx.Vout { fmt.Println(tx.Value) fmt.Println(tx.ScriptPubKey) } } //fmt.Println("Transactions:", block.Transactions) fmt.Printf("Nonce: %d\n", block.Nonce) pow := NewProofOfWork(block) fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate())) fmt.Println() if len(block.PrevBlockHash) == 0 { break } } } // send 发送 func (cli *CLI) send(from, to string, amount int) { bc := NewBlockchain(from) defer bc.db.Close() // 创建交易 tx := NewUTXOTransaction(from, to, amount, bc) // 挖矿 bc.MineBlock([]*Transaction{tx}) fmt.Println("Success!") } // 新建一个run函数,实现在程序框显示功能,并给出提示,然后根据相应的命令执行相应的操作 func (cli *CLI) run() { for { fmt.Println("1. getbalance -address ADDRESS - 获取地址的余额 ") fmt.Println("2. createblockchain -address ADDRESS - 创建区块链并将创世区块奖励发送到ADDRESS") fmt.Println("3. printchain - 打印区块链") fmt.Println("4. send -from FROM -to TO -amount AMOUNT - 从FROM地址向TO发送AMOUNT硬币") fmt.Println("5. mine ADDRESS挖矿创建区块链并将创世区块奖励发送到ADDRESS") fmt.Println("6. exit - Exit") fmt.Println("Please enter the command:") var cmd string fmt.Scanln(&cmd) switch cmd { case "1": fmt.Println("Please enter the address:") var address string fmt.Scanln(&address) cli.getBalance(address) fmt.Println() case "2": fmt.Println("Please enter the address:") var address string fmt.Scanln(&address) cli.createBlockchain(address) fmt.Println() case "3": cli.printChain() case "4": fmt.Println("Please enter the from address:") var from string fmt.Scanln(&from) fmt.Println("Please enter the to address:") var to string fmt.Scanln(&to) fmt.Println("Please enter the amount:") var amount int fmt.Scanln(&amount) cli.send(from, to, amount) fmt.Println() case "5": fmt.Println("Please enter the address:") var address string fmt.Scanln(&address) //我们要创建新区块 //挖掘新区块 //AddBlock(address) cli.addBlock(address) fmt.Println() case "6": os.Exit(0) default: fmt.Println("Invalid command") } } } // addBlock 添加区块 func (cli *CLI) addBlock(address string) { // 创建区块链 AddBlock(address) }
这段代码是整个区块链项目的入口,它创建了一个 CLI
对象,并调用其 run
方法启动命令行界面。
CLI
对象,用于处理命令行参数和执行相应操作。CLI
对象的 run
方法,启动命令行界面,并开始与用户交互。// -*- coding: utf-8 -*- // Time : 2024/4/15 22:15 // Author : blue // File : main.go // Software: Goland package main func main() { cli := CLI{} //cli.Run() cli.run() }
通过本篇博客,我们详细探讨了如何使用Go语言实现一个简易的区块链系统。我们从区块的数据结构出发,探讨了区块链的创建、交易的处理、工作量证明算法的实现,以及通过CLI与区块链进行交互的方法。此外,我们还了解了如何使用BoltDB对区块链数据进行持久化存储,这对于保证区块链数据的持久性和可靠性至关重要。
实现一个区块链系统虽然复杂,但通过逐步分析和实现,我们可以更好地理解其背后的原理和技术。希望这篇博客能为那些对区块链技术感兴趣的读者提供一定的帮助,并激发大家进一步探索和实践区块链技术的热情。
上一篇:区块链之智能合约