標籤:whether iterator end wal rom process roo int walle
交易機制:
1、區塊鏈能夠安全可靠地儲存體交易結果
2、在區塊鏈中,交易一旦被建立,就沒有任何人能夠再去修改或刪除
3、交易由一些輸入與一些輸出組合而來
4、對於一筆新的交易,它的輸入會引用之前一筆交易的輸出
5、交易的輸出,也就是比特幣實際儲存的地方
6、例外:
1、有一些輸出並沒有被關聯到某個輸入上(尚未使用)
2、一筆交易的輸入可以引用之前多筆交易的輸出
3、一個輸入必須引用一個輸出
挖礦->A通過挖礦獲得10個比特幣
-> 輸入為空白, 輸出為A獲得10個比特幣
轉賬->A向B支付3個比特幣:
找出A未用的交易(總額夠支付即可)
那麼A被找出來的這些交易就作為輸入列表,如果有剩餘的會一個找零過程(A會有一個7比特幣的輸出)
而B會有一個3比特幣的輸出
這些輸入與輸出會作為新區塊的資料存放區到鏈中
找出某人未用交易演算法:
從鏈的最新頭開始找,找出屬於某人的輸入,過濾掉與這些輸入掛鈎的輸出就是這個人的未用的交易(餘額)
transaction.go
package coreimport ("fmt""bytes""encoding/gob""log""crypto/sha256""encoding/hex")const subsidy = 10//Transactions represents a Bitcointype Transaction struct {ID []byteVin []TXInputVout []TXOutput}//TXInput represents a transaction inputtype TXInput struct {Txid []byteVout intScriptSig string}//CanUnlockOutputWith checks whether the address initiated the transactionfunc (in *TXInput) CanUnlockOutputWith(unlockingData string) bool {return in.ScriptSig == unlockingData}//TXOutput represents a transaction outputtype TXOutput struct {Value intScriptPubkey string}//SetID sets ID of a transactionfunc (tx *Transaction) SetID() {var encoder bytes.Buffervar hash [32]byteenc := gob.NewEncoder(&encoder)err := enc.Encode(tx)if err != nil {log.Panic(err)}hash = sha256.Sum256(encoder.Bytes())tx.ID = hash[:]}//NewCoinbaseTX create a new coinbase transactionfunc NewCoinBaseTX(to, data string) *Transaction {if data == "" {data = fmt.Sprintf("Reward to %s", to)}txin := TXInput{[]byte{}, -1, data}txout:= TXOutput{subsidy, to}tx := Transaction{nil, []TXInput{txin}, []TXOutput{txout}}tx.SetID()return &tx}//CanBeUnlockedWith checks if the output can be unlocked with the provided datafunc (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool {return out.ScriptPubkey == unlockingData}//IsCoinbase checks whether the transaction is coinbasefunc (tx Transaction) IsCoinbase() bool {return len(tx.Vin) == 1 && len(tx.Vin[0].Txid) == 0 && tx.Vin[0].Vout == -1}//NewUTXOTransaction creates a new transactionfunc NewUTXOTransaction(from,to string ,amount int, bc *BlockChain) *Transaction {var inputs []TXInputvar outputs []TXOutputacc,validOutputs := bc.FindSpendableOutputs(from, amount)if acc < amount {log.Panic("ERROR:Not enough funds")}//build a list of inputsfor txid,outs := range validOutputs {txID,err := hex.DecodeString(txid)if err != nil {log.Panic(err)}for _,out := range outs {input := TXInput{txID,out, from}inputs = append(inputs,input)}}//build a list of outputsoutputs = append(outputs, TXOutput{amount,to})if acc > amount {outputs = append(outputs, TXOutput{acc - amount, from})}tx := Transaction{nil, inputs, outputs}tx.SetID()return &tx}
cli.go
package coreimport ("fmt""os""flag""log""strconv")//CLI responsible for processing command line argumentstype CLI struct {}func (cli *CLI) createBlockChain(address string) {bc := CreateBlockchain(address)defer bc.Db.Close()fmt.Println("Done")}func (cli *CLI) getBalance(address string) {bc := NewBlockChain(address)defer bc.Db.Close()balance := 0UTXOs := bc.FindUTXO(address)for _,out := range UTXOs {balance += out.Value}fmt.Printf("balance of ‘%s‘ : ‘%d‘", address, balance)}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")}func (cli *CLI) printUsage() {fmt.Println("Usage:")fmt.Println(" getbalance -address ADDRESS - Get balance of 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" +" coin from FROM address to TO")}func (cli *CLI) validateArgs() {if len(os.Args) < 2 {cli.printUsage()os.Exit(1)}}func (cli *CLI) printChain() {bc := NewBlockChain("")defer bc.Db.Close()bci := bc.Iterator()for {block := bci.Next()fmt.Printf("Prev hash: %x\n", block.PrevBlockHash)fmt.Printf("Hash: %x\n", block.Hash)pow := NewProofOfWork(block)fmt.Printf("Pow: %s\n", strconv.FormatBool(pow.Validate()))fmt.Println()if len(block.PrevBlockHash) == 0 {break}}}//Run parses command line arguments and process commandsfunc (cli *CLI) Run() {cli.validateArgs()getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError)printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)sendCmd := flag.NewFlagSet("send", flag.ExitOnError)createBlockChainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError)getBalanceAddress := getBalanceCmd.String("address","", "the address" +"to get balance for")createBlockchainAddress := createBlockChainCmd.String("address","","the address to send genesis block award to")sendFrom := sendCmd.String("from", "", "Source wallet address")sendTo := sendCmd.String("to", "", "Destination wallet address")sendAmount := sendCmd.Int("amount", 0, "Amount to send")switch os.Args[1] {case "createblockchain":err := createBlockChainCmd.Parse(os.Args[2:])if err != nil {log.Panic(err)}case "printchain":err := printChainCmd.Parse(os.Args[2:])if err != nil {log.Panic(err)}case "getbalance":err := getBalanceCmd.Parse(os.Args[2:])if err != nil {log.Panic(err)}case "send":err := sendCmd.Parse(os.Args[2:])if err != nil {log.Panic(err)}default:cli.printUsage()os.Exit(1)}if getBalanceCmd.Parsed() {if *getBalanceAddress == "" {getBalanceCmd.Usage()os.Exit(1)}cli.getBalance(*getBalanceAddress)}if createBlockChainCmd.Parsed() {if *createBlockchainAddress == "" {createBlockChainCmd.Usage()os.Exit(1)}cli.createBlockChain(*createBlockchainAddress)}if sendCmd.Parsed() {if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 {sendCmd.Usage()os.Exit(1)}cli.send(*sendFrom, *sendTo, *sendAmount)}if printChainCmd.Parsed() {cli.printChain()}}
blockchain.go
package coreimport ("github.com/boltdb/bolt""log""fmt""os""encoding/hex")const dbFile = "blockchain.db"const blockBucket = "blocks"const genesisCoinbaseData = "The Time 03/Jan/2009 Chancellor on brink of second bailout for bank"//BlockChain keeps a sequence of Blockstype BlockChain struct {tip []byteDb *bolt.DB}//BlockChainIterator is used to iterator over blockchain blockstype BlockChainIterator struct {currentHash []bytedb *bolt.DB}//Iteratorfunc (bc *BlockChain) Iterator() *BlockChainIterator {bci := &BlockChainIterator{bc.tip, bc.Db}return bci}//Next returns next block starting from the tipfunc (i *BlockChainIterator) Next() *Block {var block *Blockerr := i.db.View(func(tx *bolt.Tx) error {b := tx.Bucket([]byte(blockBucket))encoderBlock := b.Get(i.currentHash)block = DeserializeBlock(encoderBlock)return nil})if err != nil {log.Panic(err)}i.currentHash = block.PrevBlockHashreturn block}func NewBlockChain(address string) *BlockChain {if dbExists() == false {fmt.Println("No Blockchain exists, create one first")os.Exit(1)}var tip []bytedb,err := bolt.Open(dbFile, 0600, nil)if err != nil {log.Panic(err)}err = db.Update(func(tx *bolt.Tx) error {db := tx.Bucket([]byte(blockBucket))tip = db.Get([]byte("l"))return nil})if err != nil {log.Panic(err)}bc := BlockChain{tip, db}return &bc}func dbExists() bool {if _,err := os.Stat(dbFile); os.IsNotExist(err) {return false}return true}//FindUTXO finds and returns all unspent transaction outputsfunc (bc *BlockChain) FindUTXO(address string) []TXOutput{var UTXOs []TXOutputunspentTransactions := bc.FindUnspentTransactions(address)for _,tx := range unspentTransactions {for _,out := range tx.Vout {if out.CanBeUnlockedWith(address) {UTXOs = append(UTXOs, out)}}}return UTXOs}//FindSpendableOutputs finds and returns unspent outputs to reference in inputsfunc (bc *BlockChain) FindSpendableOutputs(address string, amount int) (int,map[string][]int) {unspentOutputs := make(map[string][]int)unspentTXs := bc.FindUnspentTransactions(address)accumulated := 0Work:for _,tx := range unspentTXs{txID := hex.EncodeToString(tx.ID)for outIdx,out := range tx.Vout {if out.CanBeUnlockedWith(address) && accumulated < amount {accumulated += out.ValueunspentOutputs[txID] = append(unspentOutputs[txID],outIdx )if accumulated >= amount {break Work}}}}return accumulated, unspentOutputs}//FindUnspentTransactions returns a list of transactions containing unspent outputsfunc (bc *BlockChain) FindUnspentTransactions(address string) []Transaction {var unspentTXs []TransactionspentTXOs := make(map[string][]int)bci := bc.Iterator()for {block := bci.Next()for _,tx := range block.Transactions {txID := hex.EncodeToString(tx.ID)Outputs:for outIdx, out := range tx.Vout {//was the output spent?if spentTXOs[txID] != nil{for _,spentOut := range spentTXOs[txID] {if spentOut == outIdx {continue Outputs}}}if out.CanBeUnlockedWith(address) {unspentTXs = append(unspentTXs,*tx)}}if tx.IsCoinbase() == false {for _,in := range tx.Vin {if in.CanUnlockOutputWith(address) {inTxID := hex.EncodeToString(in.Txid)spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)}}}if len(block.PrevBlockHash) == 0 {break}}return unspentTXs}}//CreateBlockchain create a new blockchain DBfunc CreateBlockchain(address string) *BlockChain {if dbExists() {fmt.Println("Blockchain already exsits.")os.Exit(1)}var tip []bytedb,err := bolt.Open(dbFile, 0600, nil)if err != nil {log.Panic(err)}err = db.Update(func(tx *bolt.Tx) error {cbtx := NewCoinBaseTX(address, genesisCoinbaseData)genesis := NewGenesisBlock(cbtx)b,err := tx.CreateBucket([]byte(blockBucket))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.Hashreturn nil})if err != nil {log.Panic(err)}bc := BlockChain{tip, db}return &bc}//MineBlock mines a new block with the provided transactionsfunc (bc *BlockChain) MineBlock(transaction []*Transaction) {var lastHash []byteerr := bc.Db.View(func(tx *bolt.Tx) error {b := tx.Bucket([]byte(blockBucket))lastHash = b.Get([]byte("l"))return nil})if err != nil {log.Panic(err)}newBlock := NewBlock(transaction,lastHash)err = bc.Db.Update(func(tx *bolt.Tx) error {b := tx.Bucket([]byte(blockBucket))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)}bc.tip = newBlock.Hashreturn nil})if err != nil {log.Panic(err)}}
main.go
package mainimport "core"func main() {cli := core.CLI{}cli.Run()}
block.go
package coreimport ("time""bytes""encoding/gob""log""crypto/sha256")//Block keeps block headertype Block struct {Timestamp int64 //區塊建立的時間Transactions []*Transaction //區塊包含的資料PrevBlockHash []byte //前一個區塊的雜湊值Hash []byte //區塊自身的雜湊值,用於校正區塊資料有效Nonce int //記錄工作量證明用到的數字}func (b *Block) Serialize() []byte {var result bytes.Bufferencoder := gob.NewEncoder(&result)err := encoder.Encode(b)if err != nil {log.Panic(err)}return result.Bytes()}func DeserializeBlock(d []byte) *Block {var block Blockdecoder := gob.NewDecoder(bytes.NewReader(d))err := decoder.Decode(&block)if err != nil {log.Panic(err)}return &block}//NewBlock create and returns Blockfunc NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block {block := &Block{Timestamp: time.Now().Unix(),Transactions: transactions,PrevBlockHash: prevBlockHash,Hash: []byte{},Nonce: 0,}pow := NewProofOfWork(block) //建立工作量證明nonce,hash := pow.Run() //執行工作量證明(挖礦)block.Hash = hashblock.Nonce = noncereturn block}//NewGenesisBlock create and returns genesis Blockfunc NewGenesisBlock(coinbase *Transaction) *Block {return NewBlock([]*Transaction{coinbase}, []byte{})}//HashTransactions returns a hash of the transaction in the blockfunc (b *Block) HashTransactions() []byte {var txHashs [][]bytevar txHash [32]bytefor _,tx := range b.Transactions {txHashs = append(txHashs, tx.ID)}txHash = sha256.Sum256(bytes.Join(txHashs, []byte{}))return txHash[:]}
proofofwork.go
package coreimport ("math""math/big""fmt""crypto/sha256""bytes")var (maxNonce = math.MaxInt64)const targetBits = 16//ProofOfWork represents a proof-of-worktype ProofOfWork struct {block *Blocktarget *big.Int}//NewProofOfWork builds and returns a ProofOfWorkfunc NewProofOfWork(b *Block) *ProofOfWork {target := big.NewInt(1)target.Lsh(target,uint(256-targetBits))pow := &ProofOfWork{b, target}return pow}func (pow *ProofOfWork) prepareData(nonce int) []byte {data := bytes.Join([][]byte{pow.block.PrevBlockHash,pow.block.HashTransactions(),IntToHex(int64(pow.block.Timestamp)),IntToHex(int64(targetBits)),IntToHex(int64(nonce)),},[]byte{},)return data}func (pow *ProofOfWork) Run() (int, []byte) {var hashInt big.Intvar hash [32]bytenonce := 0fmt.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[:]}func (pow *ProofOfWork) Validate() bool {var hashInt big.Intdata := pow.prepareData(pow.block.Nonce)hash := sha256.Sum256(data)hashInt.SetBytes(hash[:])isValid := hashInt.Cmp(pow.target) == -1return isValid}
交易及記賬