标签: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 core
import (
"fmt"
"bytes"
"encoding/gob"
"log"
"crypto/sha256"
"encoding/hex"
)
const subsidy = 10
//Transactions represents a Bitcoin
type Transaction struct {
ID []byte
Vin []TXInput
Vout []TXOutput
}
//TXInput represents a transaction input
type TXInput struct {
Txid []byte
Vout int
ScriptSig string
}
//CanUnlockOutputWith checks whether the address initiated the transaction
func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool {
return in.ScriptSig == unlockingData
}
//TXOutput represents a transaction output
type TXOutput struct {
Value int
ScriptPubkey string
}
//SetID sets ID of a transaction
func (tx *Transaction) SetID() {
var encoder bytes.Buffer
var hash [32]byte
enc := 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 transaction
func 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 data
func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool {
return out.ScriptPubkey == unlockingData
}
//IsCoinbase checks whether the transaction is coinbase
func (tx Transaction) IsCoinbase() bool {
return len(tx.Vin) == 1 && len(tx.Vin[0].Txid) == 0 && tx.Vin[0].Vout == -1
}
//NewUTXOTransaction creates a new transaction
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")
}
//build a list of inputs
for 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 outputs
outputs = 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 core
import (
"fmt"
"os"
"flag"
"log"
"strconv"
)
//CLI responsible for processing command line arguments
type 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 := 0
UTXOs := 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 commands
func (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 core
import (
"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 Blocks
type BlockChain struct {
tip []byte
Db *bolt.DB
}
//BlockChainIterator is used to iterator over blockchain blocks
type BlockChainIterator struct {
currentHash []byte
db *bolt.DB
}
//Iterator
func (bc *BlockChain) Iterator() *BlockChainIterator {
bci := &BlockChainIterator{bc.tip, bc.Db}
return bci
}
//Next returns next block starting from the tip
func (i *BlockChainIterator) Next() *Block {
var block *Block
err := 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.PrevBlockHash
return block
}
func NewBlockChain(address string) *BlockChain {
if dbExists() == false {
fmt.Println("No Blockchain exists, 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 {
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 outputs
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 finds and returns unspent outputs to reference in inputs
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{
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
}
//FindUnspentTransactions returns a list of transactions containing unspent outputs
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 {
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 DB
func CreateBlockchain(address string) *BlockChain {
if dbExists() {
fmt.Println("Blockchain already exsits.")
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 {
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.Hash
return nil
})
if err != nil {
log.Panic(err)
}
bc := BlockChain{tip, db}
return &bc
}
//MineBlock mines a new block with the provided transactions
func (bc *BlockChain) MineBlock(transaction []*Transaction) {
var lastHash []byte
err := 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.Hash
return nil
})
if err != nil {
log.Panic(err)
}
}
main.go
package main
import "core"
func main() {
cli := core.CLI{}
cli.Run()
}
block.go
package core
import (
"time"
"bytes"
"encoding/gob"
"log"
"crypto/sha256"
)
//Block keeps block header
type Block struct {
Timestamp int64 //区块创建的时间
Transactions []*Transaction //区块包含的数据
PrevBlockHash []byte //前一个区块的哈希值
Hash []byte //区块自身的哈希值,用于校验区块数据有效
Nonce int //记录工作量证明用到的数字
}
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()
}
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
}
//NewBlock create and returns Block
func 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 = hash
block.Nonce = nonce
return block
}
//NewGenesisBlock create and returns genesis Block
func NewGenesisBlock(coinbase *Transaction) *Block {
return NewBlock([]*Transaction{coinbase}, []byte{})
}
//HashTransactions returns a hash of the transaction in the block
func (b *Block) HashTransactions() []byte {
var txHashs [][]byte
var txHash [32]byte
for _,tx := range b.Transactions {
txHashs = append(txHashs, tx.ID)
}
txHash = sha256.Sum256(bytes.Join(txHashs, []byte{}))
return txHash[:]
}
proofofwork.go
package core
import (
"math"
"math/big"
"fmt"
"crypto/sha256"
"bytes"
)
var (
maxNonce = math.MaxInt64
)
const targetBits = 16
//ProofOfWork represents a proof-of-work
type ProofOfWork struct {
block *Block
target *big.Int
}
//NewProofOfWork builds and returns a ProofOfWork
func 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.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[:]
}
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
}
标签:whether iterator end wal rom process roo int walle
原文地址:https://www.cnblogs.com/coder-886/p/9812165.html