经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 区块链 » 查看文章
从Go语言编码角度解释实现简易区块链交易
来源:cnblogs  作者:东寻  时间:2019/10/31 8:43:47  对本文有异议

在公链基础上实现区块链交易

区块链的目的,是能够安全可靠的存储交易,比如我们常见的比特币的交易,这里我们会以比特币为例实现区块链上的通用交易。上一节用简单的数据结构完成了区块链的公链,本节在此基础上对区块链的交易部分进行实现。实现公链

交易机制

在区块链中,交易一旦被创建,就没有任何人能够再去修改或是删除它,本节将实现一个交易的基本框架,具体交易细节将会在之后给出。

以比特币为例,不同于一般概念的账户模型,其交易采用的是UTXO模型。我们所需要的信息,都间接的包含在了每一笔交易中,包括用户的余额信息。

对于每一笔交易,你可以想象成一个通道,通道的左端有若干个输入信息,通道的右端会有若干输出信息。输入信息代表的意义是,该交易所用的币是从何而来,一条交易可以有0到多个币源(0是特殊情况,即被挖出的矿,因为没有用户来源,所以没有输入信息)。输出信息代表的意义是,进行该交易后,数字货币变动到哪里去了。因此,一条交易信息中货币的输入数量与输出数量应该是等价的,数字货币的来源总和,等于数字货币的输出总和。不难想象,与传统的账户模型相比,在UTXO模型中用户的账户余额是记录在交易的输出部分。

举个最简单的例子,假设A需要给B支付了一个比特币,将执行以下流程:

  1. 查看当前已有的交易信息,找到交易输出指向自己的交易并将余额计入总和
  2. 判断当前交易信息输出中是否有足够的数字货币属于自己
  3. 当余额不足时,提示余额不足信息
  4. 当余额充足时,新建一条交易,即一个UTXO
  5. 该UTXO的输入信息是消费用户的部分余额(不需要消费用户的所有余额,只要满足够用就行),而用户的余额是记录在之前已有的UTXO的输出中,所以新交易的输入,便是之前某些交易的输出。
  6. 当用户找到的余额数量与本次交易所需的数量不相等时,用户可以将剩下的货币再向自己输出,即找零,以保证交易的输入与输出相等

这样我们就实现了一个简单的交易,在这场交易中有货币的来源,货币有明确的去向,同时携带了我们正在进行的交易信息。

之后我们将结合代码,让这种逻辑变得更加清晰,下面这张图是对UTXO模型的简单描述:
在这里插入图片描述
Coinbase交易是特殊的一种交易,它表示矿工挖出了新的矿,作用是将新挖出的矿加入公链中并将输出指向挖矿的矿工。

该例子表示,张三挖矿得到12.5个比特币,然后支付了2.5个给李四,自己剩余10比特币,之后张三李四各支付2.5个比特币给王五,最终张三还剩7.5个比特币,李四余额用尽,王五剩余5个比特币,总和12.5等于张三挖出的总矿币。

编码实现

与之前已经完成的实现公链的代码相比,区块链的交易需要新建一个transaction.go文件,用来实现交易逻辑。其余文件中的代码,会跟随交易机制的加入进行微小的调整。

transaction.go

以下为transaction.go的代码:

  1. package main
  2. import (
  3. "bytes"
  4. "crypto/sha256"
  5. "encoding/gob"
  6. "encoding/hex"
  7. "fmt"
  8. "log"
  9. )
  10. const subsidy = 10
  11. // Transaction represents a Bitcoin transaction
  12. type Transaction struct {
  13. ID []byte
  14. Vin []TXInput
  15. Vout []TXOutput
  16. }
  17. // IsCoinbase checks whether the transaction is coinbase
  18. func (tx Transaction) IsCoinbase() bool {
  19. return len(tx.Vin) == 1 && len(tx.Vin[0].Txid) == 0 && tx.Vin[0].Vout == -1
  20. }
  21. // SetID sets ID of a transaction
  22. func (tx *Transaction) SetID() {
  23. var encoded bytes.Buffer
  24. var hash [32]byte
  25. enc := gob.NewEncoder(&encoded)
  26. err := enc.Encode(tx)
  27. if err != nil {
  28. log.Panic(err)
  29. }
  30. hash = sha256.Sum256(encoded.Bytes())
  31. tx.ID = hash[:]
  32. }
  33. // TXInput represents a transaction input
  34. type TXInput struct {
  35. Txid []byte
  36. Vout int
  37. ScriptSig string
  38. }
  39. // TXOutput represents a transaction output
  40. type TXOutput struct {
  41. Value int
  42. ScriptPubKey string
  43. }
  44. // CanUnlockOutputWith checks whether the address initiated the transaction
  45. func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool {
  46. return in.ScriptSig == unlockingData
  47. }
  48. // CanBeUnlockedWith checks if the output can be unlocked with the provided data
  49. func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool {
  50. return out.ScriptPubKey == unlockingData
  51. }
  52. // NewCoinbaseTX creates a new coinbase transaction
  53. func NewCoinbaseTX(to, data string) *Transaction {
  54. if data == "" {
  55. data = fmt.Sprintf("Reward to '%s'", to)
  56. }
  57. txin := TXInput{[]byte{}, -1, data}
  58. txout := TXOutput{subsidy, to}
  59. tx := Transaction{nil, []TXInput{txin}, []TXOutput{txout}}
  60. tx.SetID()
  61. return &tx
  62. }
  63. // NewUTXOTransaction creates a new transaction
  64. func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction {
  65. var inputs []TXInput
  66. var outputs []TXOutput
  67. acc, validOutputs := bc.FindSpendableOutputs(from, amount)
  68. if acc < amount {
  69. log.Panic("ERROR: Not enough funds")
  70. }
  71. // Build a list of inputs
  72. for txid, outs := range validOutputs {
  73. txID, err := hex.DecodeString(txid)
  74. if err != nil {
  75. log.Panic(err)
  76. }
  77. for _, out := range outs {
  78. input := TXInput{txID, out, from}
  79. inputs = append(inputs, input)
  80. }
  81. }
  82. // Build a list of outputs
  83. outputs = append(outputs, TXOutput{amount, to})
  84. if acc > amount {
  85. outputs = append(outputs, TXOutput{acc - amount, from}) // a change
  86. }
  87. tx := Transaction{nil, inputs, outputs}
  88. tx.SetID()
  89. return &tx
  90. }

代码主要包含以下内容:

  • Transaction 结构体,包含当前交易的ID(交易需要ID)、输入数组以及输出数组
  • IsCoinbase函数,用来判断当前交易是否是Coinbase交易(挖矿交易)
  • SetID函数给交易设置id
  • TXInput 结构体,包含输入的某条交易的id,该交易某个输出的金额与地址
  • TXOutput 结构体,包含当前交易的某个输出的金额与地址
  • CanUnlockOutputWith函数判断提供的地址能否匹配某条交易记录的输入地址
  • CanBeUnlockedWith函数判断提供的地址能否匹配某条交易记录的输出地址
  • NewCoinbaseTX函数创建一条挖矿交易
  • NewUTXOTransaction函数创建一条新的交易

关于TXInput与TXOutput中地址的问题,因为目前还没有实现区块链中的地址,所以本节涉及的地址直接用字符串代替,验证地址也只是进行了字符串对比。地址是必要的,它标注了当前的余额属于谁,这里因为刚实现交易机制,还没有引入真正的地址机制,所以是存在漏洞的,用户只要知道有哪些用户就可以直接往自己地址转钱,在下一节会实现地址机制进行完善。

block.go

在transaction.go中实现了交易的结构体,如何创建一条新的交易,以及简单的交易对象判断。在其余文件中,block.go文件做了一些改动,主要是将原本的data字符串换成了Transaction交易。同样的,下一节中我们会将本节的地址字符串换成相应机制的地址,以下是改动后的block.go文件:

  1. package main
  2. import (
  3. "bytes"
  4. "crypto/sha256"
  5. "encoding/gob"
  6. "log"
  7. "time"
  8. )
  9. // Block keeps block headers
  10. type Block struct {
  11. Timestamp int64
  12. Transactions []*Transaction
  13. PrevBlockHash []byte
  14. Hash []byte
  15. Nonce int
  16. }
  17. // Serialize serializes the block
  18. func (b *Block) Serialize() []byte {
  19. var result bytes.Buffer
  20. encoder := gob.NewEncoder(&result)
  21. err := encoder.Encode(b)
  22. if err != nil {
  23. log.Panic(err)
  24. }
  25. return result.Bytes()
  26. }
  27. // HashTransactions returns a hash of the transactions in the block
  28. func (b *Block) HashTransactions() []byte {
  29. var txHashes [][]byte
  30. var txHash [32]byte
  31. for _, tx := range b.Transactions {
  32. txHashes = append(txHashes, tx.ID)
  33. }
  34. txHash = sha256.Sum256(bytes.Join(txHashes, []byte{}))
  35. return txHash[:]
  36. }
  37. // NewBlock creates and returns Block
  38. func NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block {
  39. block := &Block{time.Now().Unix(), transactions, prevBlockHash, []byte{}, 0}
  40. pow := NewProofOfWork(block)
  41. nonce, hash := pow.Run()
  42. block.Hash = hash[:]
  43. block.Nonce = nonce
  44. return block
  45. }
  46. // NewGenesisBlock creates and returns genesis Block
  47. func NewGenesisBlock(coinbase *Transaction) *Block {
  48. return NewBlock([]*Transaction{coinbase}, []byte{})
  49. }
  50. // DeserializeBlock deserializes a block
  51. func DeserializeBlock(d []byte) *Block {
  52. var block Block
  53. decoder := gob.NewDecoder(bytes.NewReader(d))
  54. err := decoder.Decode(&block)
  55. if err != nil {
  56. log.Panic(err)
  57. }
  58. return &block
  59. }

添加了HashTransactions函数,用来将交易转换成哈希值,其余函数随结构体中Data->Transactions的变动相应调整。

blockchain.go

在blockchain.go中,涉及到寻找用户余额(未花费交易输出)操作,需要多做一些调整:

  1. package main
  2. import (
  3. "encoding/hex"
  4. "fmt"
  5. "log"
  6. "os"
  7. "bolt-master"
  8. )
  9. const dbFile = "blockchain.db"
  10. const blocksBucket = "blocks"
  11. const genesisCoinbaseData = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
  12. // Blockchain implements interactions with a DB
  13. type Blockchain struct {
  14. tip []byte
  15. db *bolt.DB
  16. }
  17. // BlockchainIterator is used to iterate over blockchain blocks
  18. type BlockchainIterator struct {
  19. currentHash []byte
  20. db *bolt.DB
  21. }
  22. // MineBlock mines a new block with the provided transactions
  23. func (bc *Blockchain) MineBlock(transactions []*Transaction) {
  24. var lastHash []byte
  25. err := bc.db.View(func(tx *bolt.Tx) error {
  26. b := tx.Bucket([]byte(blocksBucket))
  27. lastHash = b.Get([]byte("l"))
  28. return nil
  29. })
  30. if err != nil {
  31. log.Panic(err)
  32. }
  33. newBlock := NewBlock(transactions, lastHash)
  34. err = bc.db.Update(func(tx *bolt.Tx) error {
  35. b := tx.Bucket([]byte(blocksBucket))
  36. err := b.Put(newBlock.Hash, newBlock.Serialize())
  37. if err != nil {
  38. log.Panic(err)
  39. }
  40. err = b.Put([]byte("l"), newBlock.Hash)
  41. if err != nil {
  42. log.Panic(err)
  43. }
  44. bc.tip = newBlock.Hash
  45. return nil
  46. })
  47. }
  48. // FindUnspentTransactions returns a list of transactions containing unspent outputs
  49. func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction {
  50. var unspentTXs []Transaction
  51. spentTXOs := make(map[string][]int)
  52. bci := bc.Iterator()
  53. for {
  54. block := bci.Next()
  55. for _, tx := range block.Transactions {
  56. txID := hex.EncodeToString(tx.ID)
  57. Outputs:
  58. for outIdx, out := range tx.Vout {
  59. // Was the output spent?
  60. if spentTXOs[txID] != nil {
  61. for _, spentOut := range spentTXOs[txID] {
  62. if spentOut == outIdx {
  63. continue Outputs
  64. }
  65. }
  66. }
  67. if out.CanBeUnlockedWith(address) {
  68. unspentTXs = append(unspentTXs, *tx)
  69. }
  70. }
  71. if tx.IsCoinbase() == false {
  72. for _, in := range tx.Vin {
  73. if in.CanUnlockOutputWith(address) {
  74. inTxID := hex.EncodeToString(in.Txid)
  75. spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
  76. }
  77. }
  78. }
  79. }
  80. if len(block.PrevBlockHash) == 0 {
  81. break
  82. }
  83. }
  84. return unspentTXs
  85. }
  86. // FindUTXO finds and returns all unspent transaction outputs
  87. func (bc *Blockchain) FindUTXO(address string) []TXOutput {
  88. var UTXOs []TXOutput
  89. unspentTransactions := bc.FindUnspentTransactions(address)
  90. for _, tx := range unspentTransactions {
  91. for _, out := range tx.Vout {
  92. if out.CanBeUnlockedWith(address) {
  93. UTXOs = append(UTXOs, out)
  94. }
  95. }
  96. }
  97. return UTXOs
  98. }
  99. // FindSpendableOutputs finds and returns unspent outputs to reference in inputs
  100. func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) {
  101. unspentOutputs := make(map[string][]int)
  102. unspentTXs := bc.FindUnspentTransactions(address)
  103. accumulated := 0
  104. Work:
  105. for _, tx := range unspentTXs {
  106. txID := hex.EncodeToString(tx.ID)
  107. for outIdx, out := range tx.Vout {
  108. if out.CanBeUnlockedWith(address) && accumulated < amount {
  109. accumulated += out.Value
  110. unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
  111. if accumulated >= amount {
  112. break Work
  113. }
  114. }
  115. }
  116. }
  117. return accumulated, unspentOutputs
  118. }
  119. // Iterator returns a BlockchainIterat
  120. func (bc *Blockchain) Iterator() *BlockchainIterator {
  121. bci := &BlockchainIterator{bc.tip, bc.db}
  122. return bci
  123. }
  124. // Next returns next block starting from the tip
  125. func (i *BlockchainIterator) Next() *Block {
  126. var block *Block
  127. err := i.db.View(func(tx *bolt.Tx) error {
  128. b := tx.Bucket([]byte(blocksBucket))
  129. encodedBlock := b.Get(i.currentHash)
  130. block = DeserializeBlock(encodedBlock)
  131. return nil
  132. })
  133. if err != nil {
  134. log.Panic(err)
  135. }
  136. i.currentHash = block.PrevBlockHash
  137. return block
  138. }
  139. func dbExists() bool {
  140. if _, err := os.Stat(dbFile); os.IsNotExist(err) {
  141. return false
  142. }
  143. return true
  144. }
  145. // NewBlockchain creates a new Blockchain with genesis Block
  146. func NewBlockchain(address string) *Blockchain {
  147. if dbExists() == false {
  148. fmt.Println("No existing blockchain found. Create one first.")
  149. os.Exit(1)
  150. }
  151. var tip []byte
  152. db, err := bolt.Open(dbFile, 0600, nil)
  153. if err != nil {
  154. log.Panic(err)
  155. }
  156. err = db.Update(func(tx *bolt.Tx) error {
  157. b := tx.Bucket([]byte(blocksBucket))
  158. tip = b.Get([]byte("l"))
  159. return nil
  160. })
  161. if err != nil {
  162. log.Panic(err)
  163. }
  164. bc := Blockchain{tip, db}
  165. return &bc
  166. }
  167. // CreateBlockchain creates a new blockchain DB
  168. func CreateBlockchain(address string) *Blockchain {
  169. if dbExists() {
  170. fmt.Println("Blockchain already exists.")
  171. os.Exit(1)
  172. }
  173. var tip []byte
  174. db, err := bolt.Open(dbFile, 0600, nil)
  175. if err != nil {
  176. log.Panic(err)
  177. }
  178. err = db.Update(func(tx *bolt.Tx) error {
  179. cbtx := NewCoinbaseTX(address, genesisCoinbaseData)
  180. genesis := NewGenesisBlock(cbtx)
  181. b, err := tx.CreateBucket([]byte(blocksBucket))
  182. if err != nil {
  183. log.Panic(err)
  184. }
  185. err = b.Put(genesis.Hash, genesis.Serialize())
  186. if err != nil {
  187. log.Panic(err)
  188. }
  189. err = b.Put([]byte("l"), genesis.Hash)
  190. if err != nil {
  191. log.Panic(err)
  192. }
  193. tip = genesis.Hash
  194. return nil
  195. })
  196. if err != nil {
  197. log.Panic(err)
  198. }
  199. bc := Blockchain{tip, db}
  200. return &bc
  201. }

代码的主要变动是新增了三个关于交易的函数:

  • FindUnspendTransactions遍历公链,寻找交易信息中没有被使用过输出的交易,即未被花费过的余额。当一条交易中的余额被其他交易用做过输入,该余额也就不在具有余额的属性,不能再次被交易
  • FindUTXO在内部调用了FindUnspendTransactions函数,与FindUnspendTransactions不同的是它用于查询用户的余额信息,即所有有效未花费余额的总和
  • FindSpendableOutputs在内部调用了FindUnspendTransactions函数,用于找出哪些余额是可用的

其次,原本的Addblock被改成了更具体的Mineblock挖矿函数,新增了Createblockchain函数和dbExists函数,用来判断数据库是否存在,只有当数据库中没有公链时才能创建新的区块链。

proofofwork.go

在proofofwork文件中,仅在prepareData时将Data换成了HashTransactions,在挖矿时不再打印Data部分,proofofwork.go完整代码如下:

  1. package main
  2. import (
  3. "bytes"
  4. "crypto/sha256"
  5. "fmt"
  6. "math"
  7. "math/big"
  8. )
  9. var (
  10. maxNonce = math.MaxInt64
  11. )
  12. const targetBits = 24
  13. // ProofOfWork represents a proof-of-work
  14. type ProofOfWork struct {
  15. block *Block
  16. target *big.Int
  17. }
  18. // NewProofOfWork builds and returns a ProofOfWork
  19. func NewProofOfWork(b *Block) *ProofOfWork {
  20. target := big.NewInt(1)
  21. target.Lsh(target, uint(256-targetBits))
  22. pow := &ProofOfWork{b, target}
  23. return pow
  24. }
  25. func (pow *ProofOfWork) prepareData(nonce int) []byte {
  26. data := bytes.Join(
  27. [][]byte{
  28. pow.block.PrevBlockHash,
  29. pow.block.HashTransactions(),
  30. IntToHex(pow.block.Timestamp),
  31. IntToHex(int64(targetBits)),
  32. IntToHex(int64(nonce)),
  33. },
  34. []byte{},
  35. )
  36. return data
  37. }
  38. // Run performs a proof-of-work
  39. func (pow *ProofOfWork) Run() (int, []byte) {
  40. var hashInt big.Int
  41. var hash [32]byte
  42. nonce := 0
  43. fmt.Printf("Mining a new block")
  44. for nonce < maxNonce {
  45. data := pow.prepareData(nonce)
  46. hash = sha256.Sum256(data)
  47. // fmt.Printf("\r%x", hash)
  48. hashInt.SetBytes(hash[:])
  49. if hashInt.Cmp(pow.target) == -1 {
  50. break
  51. } else {
  52. nonce++
  53. }
  54. }
  55. // fmt.Print("\n\n")
  56. return nonce, hash[:]
  57. }
  58. // Validate validates block's PoW
  59. func (pow *ProofOfWork) Validate() bool {
  60. var hashInt big.Int
  61. data := pow.prepareData(pow.block.Nonce)
  62. hash := sha256.Sum256(data)
  63. hashInt.SetBytes(hash[:])
  64. isValid := hashInt.Cmp(pow.target) == -1
  65. return isValid
  66. }

cli.go

cli.go文件随底层的一些变动,做出相应的业务逻辑改变,变动主要用于实现命令行操作,不涉及区块链的逻辑:

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "log"
  6. "os"
  7. "strconv"
  8. )
  9. // CLI responsible for processing command line arguments
  10. type CLI struct{}
  11. func (cli *CLI) createBlockchain(address string) {
  12. bc := CreateBlockchain(address)
  13. bc.db.Close()
  14. fmt.Println("Done!")
  15. }
  16. func (cli *CLI) getBalance(address string) {
  17. bc := NewBlockchain(address)
  18. defer bc.db.Close()
  19. balance := 0
  20. UTXOs := bc.FindUTXO(address)
  21. for _, out := range UTXOs {
  22. balance += out.Value
  23. }
  24. fmt.Printf("Balance of '%s': %d\n", address, balance)
  25. }
  26. func (cli *CLI) printUsage() {
  27. fmt.Println("Usage:")
  28. fmt.Println(" getbalance -address ADDRESS - Get balance of ADDRESS")
  29. fmt.Println(" createblockchain -address ADDRESS - Create a blockchain and send genesis block reward to ADDRESS")
  30. fmt.Println(" printchain - Print all the blocks of the blockchain")
  31. fmt.Println(" send -from FROM -to TO -amount AMOUNT - Send AMOUNT of coins from FROM address to TO")
  32. }
  33. func (cli *CLI) validateArgs() {
  34. if len(os.Args) < 2 {
  35. cli.printUsage()
  36. os.Exit(1)
  37. }
  38. }
  39. func (cli *CLI) printChain() {
  40. // TODO: Fix this
  41. bc := NewBlockchain("")
  42. defer bc.db.Close()
  43. bci := bc.Iterator()
  44. for {
  45. block := bci.Next()
  46. fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)
  47. fmt.Printf("Hash: %x\n", block.Hash)
  48. pow := NewProofOfWork(block)
  49. fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate()))
  50. fmt.Println()
  51. if len(block.PrevBlockHash) == 0 {
  52. break
  53. }
  54. }
  55. }
  56. func (cli *CLI) send(from, to string, amount int) {
  57. bc := NewBlockchain(from)
  58. defer bc.db.Close()
  59. tx := NewUTXOTransaction(from, to, amount, bc)
  60. bc.MineBlock([]*Transaction{tx})
  61. fmt.Println("Success!")
  62. }
  63. // Run parses command line arguments and processes commands
  64. func (cli *CLI) Run() {
  65. cli.validateArgs()
  66. getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError)
  67. createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError)
  68. sendCmd := flag.NewFlagSet("send", flag.ExitOnError)
  69. printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)
  70. getBalanceAddress := getBalanceCmd.String("address", "", "The address to get balance for")
  71. createBlockchainAddress := createBlockchainCmd.String("address", "", "The address to send genesis block reward to")
  72. sendFrom := sendCmd.String("from", "", "Source wallet address")
  73. sendTo := sendCmd.String("to", "", "Destination wallet address")
  74. sendAmount := sendCmd.Int("amount", 0, "Amount to send")
  75. switch os.Args[1] {
  76. case "getbalance":
  77. err := getBalanceCmd.Parse(os.Args[2:])
  78. if err != nil {
  79. log.Panic(err)
  80. }
  81. case "createblockchain":
  82. err := createBlockchainCmd.Parse(os.Args[2:])
  83. if err != nil {
  84. log.Panic(err)
  85. }
  86. case "printchain":
  87. err := printChainCmd.Parse(os.Args[2:])
  88. if err != nil {
  89. log.Panic(err)
  90. }
  91. case "send":
  92. err := sendCmd.Parse(os.Args[2:])
  93. if err != nil {
  94. log.Panic(err)
  95. }
  96. default:
  97. cli.printUsage()
  98. os.Exit(1)
  99. }
  100. if getBalanceCmd.Parsed() {
  101. if *getBalanceAddress == "" {
  102. getBalanceCmd.Usage()
  103. os.Exit(1)
  104. }
  105. cli.getBalance(*getBalanceAddress)
  106. }
  107. if createBlockchainCmd.Parsed() {
  108. if *createBlockchainAddress == "" {
  109. createBlockchainCmd.Usage()
  110. os.Exit(1)
  111. }
  112. cli.createBlockchain(*createBlockchainAddress)
  113. }
  114. if printChainCmd.Parsed() {
  115. cli.printChain()
  116. }
  117. if sendCmd.Parsed() {
  118. if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 {
  119. sendCmd.Usage()
  120. os.Exit(1)
  121. }
  122. cli.send(*sendFrom, *sendTo, *sendAmount)
  123. }
  124. }

main.go

在main.go中,我们将所有的操作有交给cli对象进行,原本旧main.go中的新建创世块操作,也放到了cli.go的逻辑中,所以只需要以下代码:

  1. package main
  2. func main() {
  3. bc := NewBlockchain()
  4. defer bc.db.Close()
  5. cli := CLI{bc}
  6. cli.Run()
  7. }

utils.go

没有新的工具函数引入,utils.go文件不变。

在下一节,将实现区块链的地址机制,逐步完善整个区块链。

原文链接:http://www.cnblogs.com/ustca/p/11765349.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号