go区块链公链实战0x05交易(1)

论坛 期权论坛 区块链     
chaors   2018-11-20 23:30   3553   0
            区块链区块的作用是打包链上产生的交易,可以说交易是区块链至关重要的一个组成部分.在区块链中,交易一旦被创建,就没有任何人能够再去修改或是删除它.
关于交易的结构,可以参照以前的这篇:比特币源码研读(3)数据结构-交易Transaction
我们之前定义的区块结构简单地用一个字符串描述交易内容,今后将正式用新的结构来表示交易:
  1. type Block struct {    //1.区块高度    Height int64    //2.上一个区块HAsh    PrevBlockHash []byte    //3.交易数据    //Data []byte    //只前简单地对交易的描述        Txs [] *Transaction   //Transaction结构数组表示交易    //4.时间戳    Timestamp int64    //5.Hash    Hash []byte    //6.Nonce  符合工作量证明的随机数    Nonce int64}
复制代码
[h2]Transaction结构[/h2]
  1. type Transaction struct {    //1.交易哈希值    TxHAsh []byte    //2.交易输入    Vins []*TXInput    //3.交易输出    Vouts []*TXOutput}
复制代码
[h2]TXInput[/h2]
  1. type TXInput struct {    //交易ID 引用上一笔交易输出作为输入    TxHash []byte    //存储TXOutput在Vout里的索引    Vout int    //数字签名  暂时可理解为用户名    ScriptSig string}
复制代码
交易输入作为本次交易的消费源,输入来源于之前交易的输出.如上,TxHash是引用的上一笔输出所在的交易的交易哈希;Vout是该输出在相应交易中的输出索引;ScriptSig,可以暂时理解为用户名,表示哪一个用户拥有这一笔输入.ScriptSig的设定是为了保证用户只能话费自己名下的代币.
[h2]TXOutput[/h2]
  1. type TXOutput struct {    //面值    Value int64    //暂时理解为用户名    ScriptPubKey string}
复制代码
这里的交易输出就是上面交易输入里引用的输出.Value是该输出的面值,ScriptPubKey暂时理解为用户名,表示谁将拥有这笔输出.
了解比特币的人都知道,交易输出是一个完整的不可分割的结构.什么意思呢?就是我们在引用TXOutput,必须全部引用,不能仅仅使用其一部分.举个简单的:
假如你有一个25btc的TXOutput,你需要花费10btc.这个交易的过程并不是:你花费了25btc中的10btc,你的原有TXOutput依旧有15btc的余额.真正的过程是,你花费了整个原有的TXOutput,由于消费额不匹配,这里会产生一个15btc的找零.消费的结果是:你25btc的TXOutput被话费已不复存在,系统重新为你生成一个15btc面值的TXOutput.这两个TXOutput是完全不同的两个对象!!!
[h2]CoinbaseTransaction[/h2]我们知道,当矿工成功挖到一个区块时会获得一笔奖励.那么这笔奖励是怎么交付到矿工账户的.这就有赖于一笔叫做创币交易的交易.
创币交易是区块内的第一笔交易,它负责将系统产生的奖励给挖出区块的矿工.由于它并不是普通意义上的转账,所以交易输入里并不需要引用任何一笔交易输出.
  1. //创建创币交易func NewCoinbaseTransaction(address string) *Transaction {    //交易输入  由于区块第一笔交易其实没有输入,所以交易哈希传空,TXOutput索引传-1,签名随你    txInput := &TXInput{        []byte{},        -1,        "CoinbaseTransaction",    }    //交易输出  产生一笔奖励给挖矿者address    txOutput := &TXOutput{25, address}    txCoinbase := &Transaction{        []byte{},   //暂时将交易哈希置空        []*TXInput{txInput},        []*TXOutput{txOutput},    }            //交易哈希的计算    txCoinbase.HashTransactions()    return txCoinbase}
复制代码
[h2]HashTransactions[/h2]
  1. //将交易信息转换为字节数组func (tx *Transaction) HashTransactions() {    //交易信息序列化    var result bytes.Buffer    encoder := gob.NewEncoder(&result)    err := encoder.Encode(tx)    if err != nil {        log.Panic(err)    }    //设置hash    txHash := sha256.Sum256(result.Bytes())    tx.TxHAsh = txHash[:]}
复制代码
[h2]NewBlock改动[/h2]
  1. //1.创建新的区块func NewBlock(txs []*Transaction, height int64, prevBlockHash []byte) *Block {    //创建区块    block := &Block{        Height:        height,        PrevBlockHash: prevBlockHash,        Txs:           txs,        Timestamp:     time.Now().Unix(),        Hash:          nil,        Nonce:         0}    //调用工作量证明返回有效的Hash    pow := NewProofOfWork(block)    hash, nonce := pow.Run()    block.Hash = hash[:]    block.Nonce = nonce    fmt.Printf("\r######%d-%x\n", nonce, hash)    return block}
复制代码
[h2]CreateBlockchainWithGensisBlock改动[/h2]
  1. //1.创建创世区块func CreateBlockchainWithGensisBlock(address string) {    //判断数据库是否存在    if IsDBExists(dbName) {        fmt.Println("创世区块已存在...")        os.Exit(1)        //创建并打开数据库        db, err := bolt.Open(dbName, 0600, nil)        if err != nil {            log.Fatal(err)        }        var block *Block        err = db.View(func(tx *bolt.Tx) error {            b := tx.Bucket([]byte(blockTableName))            if b != nil {                hash := b.Get([]byte(newestBlockKey))                block = DeSerializeBlock(b.Get(hash))                fmt.Printf("\r######%d-%x\n", block.Nonce, hash)            }            return nil        })        if err != nil {            log.Panic(err)        }        os.Exit(1)    }    fmt.Println("正在创建创世区块...")    //创建并打开数据库    db, err := bolt.Open(dbName, 0600, nil)    if err != nil {        log.Fatal(err)    }    err = db.Update(func(tx *bolt.Tx) error {        b, err := tx.CreateBucket([]byte(blockTableName))        if err != nil {            log.Panic(err)        }        if b != nil {            //创币交易            txCoinbase := NewCoinbaseTransaction(address)            //创世区块            gensisBlock := CreateGenesisBlock([]*Transaction{txCoinbase})            //存入数据库            err := b.Put(gensisBlock.Hash, gensisBlock.Serialize())            if err != nil {                log.Panic(err)            }            //存储最新区块hash            err = b.Put([]byte(newestBlockKey), gensisBlock.Hash)            if err != nil {                log.Panic(err)            }        }        return nil    })    //更新数据库失败    if err != nil {        log.Fatal(err)    }}
复制代码
[h2]POW/prepareData改动[/h2]添加交易后,POW挖矿时也必须相应地把交易信息考虑进去.这里需要改动prepareData方法
  1. //拼接区块属性,返回字节数组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)),            IntToHex(int64(pow.Block.Height)),        },        []byte{},    )    return data}
复制代码
这里POW计算目标哈希并不需要将所有的交易信息拼接,我们只需要将每一个交易的交易哈希拼接起来即可.因为,交易哈希是交易所有信息的哈希值.这样做也能保证交易信息的完整性.所以,我们需要在Block新增一个方法:
  1. //将交易信息转换为字节数组func (block *Block) HashTransactions() []byte  {    var txHashes [][]byte    var txHash [32]byte    for _, tx := range block.Txs {        txHashes = append(txHashes, tx.TxHAsh)    }    txHash = sha256.Sum256(bytes.Join(txHashes, []byte{}))    return txHash[:]}
复制代码
[h2]Printchain添加交易信息打印[/h2]现在,整个交易的数据结构就搭起来了.我们再改动区块链打印方法,将区块的交易信息添加到打印.
  1. //3.X 优化区块链遍历方法func (blc *Blockchain) Printchain() {    //迭代器    blcIterator := blc.Iterator()    for {        block := blcIterator.Next()        fmt.Println("------------------------------")        fmt.Printf("Height:%d\n", block.Height)        fmt.Printf("PrevBlockHash:%x\n", block.PrevBlockHash)        fmt.Printf("Timestamp:%s\n", time.Unix(block.Timestamp, 0).Format("2006-01-02 03:04:05 PM"))        fmt.Printf("Hash:%x\n", block.Hash)        fmt.Printf("Nonce:%d\n", block.Nonce)        fmt.Println("Txs:")        for _,tx := range block.Txs {            fmt.Printf("%x\n", tx.TxHAsh)            fmt.Println("Vins:")            for _,in := range tx.Vins  {                fmt.Printf("txHash:%x\n", in.TxHash)                fmt.Printf("Vout:%d\n", in.Vout)                fmt.Printf("ScriptSig:%s\n\n", in.ScriptSig)            }            fmt.Println("Vouts:")            for _,out := range tx.Vouts  {                fmt.Printf("Value:%x\n", out.Value)                fmt.Printf("ScriptPubKey:%x\n\n", out.ScriptPubKey)            }        }        fmt.Println("------------------------------")        var hashInt big.Int        hashInt.SetBytes(block.PrevBlockHash)        if big.NewInt(0).Cmp(&hashInt) == 0 {            break        }    }}
复制代码
[h2]Main_Test[/h2]
  1. package mainimport (    "chaors.com/LearnGo/publicChaorsChain/part7-transaction-Prototype/BLC")func main() {    cli := BLC.CLI{}    cli.Run()}
复制代码
打印创世区块的结果为:

main_test源代码在这,喜欢的朋友记得给个小star,或者fork.也欢迎大家一起探讨区块链相关知识,一起进步!
.
.
.
.
[h2]互联网颠覆世界,区块链颠覆互联网![/h2]
---------------------------------------------20180703 20:40
         
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:70
帖子:14
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP