1. 系統流程圖
這是代碼要實作的功能

2. 主函式
func main () {
//創建一個區塊鏈, 指定輸出地址
bc := NewBlockChain("1KzwEHm9adpgyT3DhDQPX7m99wQ4juXtiw")
//呼叫命令列命令
cli := CLI{bc}
//處理相應請求
cli.Run()
}
3. 命令列函式
type CLI struct {
bc *BlockChain
}
const Usage = `
printChain 正向列印區塊鏈
printChainR 反向列印區塊鏈
getBalance --address ADDRESS 獲取指定地址的余額"
send FROM TO AMONUNT MINER DATA 由FROM轉AMOUNT給TO,由MINER挖礦,同時寫入DATA
newWallet 創建一個新的錢包(私鑰公鑰對)
listAddresses 列舉所有的錢包地址
`
//根據命令列引數處理對應請求
func (cli *CLI) Run() {
cmd := args[1]
switch cmd {
case "printChain":
//列印區塊
fmt.Printf("正向列印區塊\n")
cli.PrintBlockChain()
case "printChainR":
//反向列印區塊
fmt.Printf("反向列印區塊\n")
cli.PrintBlockChainReverse()
case "getBalance":
//獲取余額
fmt.Printf("獲取余額\n")
if len(args) == 4 && args[2] == "--address" {
address := args[3]
cli.GetBalance(address)
}
case "send":
from := args[2]
to := args[3]
amount, _ := strconv.ParseFloat(os.Args[4], 64)
miner := args[5]
data := args[6]
cli.Send(from, to, amount, miner, data)
case "newWallet":
fmt.Printf("創建新的錢包...\n")
cli.NewWallet()
case "listAddresses":
fmt.Printf("列舉所有地址...\n")
cli.ListAddresses()
default:
fmt.Printf("無效命令,請檢查!")
fmt.Printf(Usage)
}
}
4. 區塊鏈列印功能
//正向列印
func (cli *CLI)PrintBlockChain(){
cli.bc.Printchain()
fmt.Printf("列印區塊完成!\n")
}
//反向列印
func (cli *CLI) PrintBlockChainReverse(){
bc := cli.bc
//創建迭代器
it := bc.NewIterator()
for{
//回傳區塊并左移
block := it.Next()
for _, tx := range block.Transactions{
fmt.Println(tx)
}
if len(block.PrevHash) == 0 {
fmt.Printf("區塊遍歷完成!\n")
break
}
}
}
5. 獲取賬戶余額

func (cli *CLI)GetBalance(address string){
//1. 校驗地址
if !IsValidAddress(address){
fmt.Printf("地址無效:%s\n", address)
return
}
//2. 生成公鑰哈希
pubKeyHash := GetPubKeyFromAddress(address)
//3. 通過公鑰哈希找到所有的UTXO
utxos := cli.bc.FindUTXOs(pubKeyHash)
total := 0.0
//4. 遍歷UTXO,得到余額
for _, utxo := range utxos{
total += utxo.Value
}
fmt.Printf("\"%s\"的余額為:%f\n", address, total)
}
6. 創建錢包

func(cli *CLI)NewWallet(){
//1.加載本地錢包
ws := NewWallets()
//2.創建新的錢包
address := ws.CreateWallet()
fmt.Printf("地址:%s\n", address)
}
func (ws *Wallets) CreateWallet() string{
wallet := NewWalet()
//3.為新錢包生成地址
address := wallet.NewAdress()
ws.WalletsMap[address] = wallet
//4.保存新錢包到錢包集和本地
ws.savaToFile()
return address
}
7. 獲取所有錢包地址

func (cli *CLI) ListAddresses(){
//1.加載本地錢包
ws := NewWallets()
//2.遍歷所有地址
addresses := ws.ListAllAddress()
for _, address := range addresses{
fmt.Printf("地址:%s\n", address)
}
}
8. 轉賬

func (cli *CLI) Send(from, to string, amount float64, miner, data string){
//1. 校驗地址
if !IsValidAddress(from){
fmt.Printf("地址無效 from:%s\n", from)
return
}
//2. 創建挖礦交易
coinbase := NewCoinbaseTX(miner, data)
//3. 創建一個普通交易
tx := NewTransaction(from, to, amount, cli.bc)
if tx == nil {
fmt.Printf("無效交易!\n")
return
}
//4. 添加到區塊
cli.bc.AddBlock([]*Transaction{coinbase, tx})
fmt.Printf("轉賬成功!\n")
}
8.1 創建普通交易

func NewTransaction(from, to string, amount float64, bc *BlockChain) *Transaction{
//通過錢包得到公私鑰,簽名時使用
ws := NewWallets()
wallet := ws.WalletsMap[from]
if wallet == nil {
fmt.Printf("沒有找到該地址的錢包,交易創建失敗!\n")
return nil
}
pubKey := wallet.PubKey
privateKey := wallet.Private
//得到公鑰哈希
pubKeyHash := HashPubKey(pubKey)
//1.找到最合理的UTXO集合
utxos, resValue := bc.FindNeedUTXOs(pubKeyHash, amount)
if resValue < amount{
fmt.Printf("余額不足,交易失敗!\n")
return nil
}
var inputs []TXInput
var outputs []TXOutput
//2.創建交易輸入,將這些UTXO逐一轉成inputs
for id, indexArray := range utxos{
for _, i := range indexArray{
input := TXInput{
Txid: []byte(id),
Index: int64(i),
Signature: nil,
PubKey: pubKey,
}
inputs = append(inputs, input)
}
}
//3.創建交易輸出
output := NewTXOutput(amount, to)
outputs= append(outputs, *output)
//4.找零
if resValue > amount{
output = NewTXOutput(resValue - amount, from)
outputs = append(outputs, *output)
}
tx := Transaction{
TXID: []byte{},
TXInputs: inputs,
TXOutputs: outputs,
}
//5.設定交易哈希
tx.SetHash()
//6.對交易簽名
bc.SignTransaction(&tx, privateKey)
return &tx
}
8.2 找到合理的UTXO
func (bc *BlockChain) FindNeedUTXOs(senderPubKeyHash []byte, amount float64) (map[string][]uint64, float64) {
//找到的合理uutxos集合
utxos := make(map[string][]uint64)
//找到的utxos里面包含的總數
var calc float64
//1.獲取轉賬者的所有交易
txs := bc.FindUTXOTransactions(senderPubKeyHash)
for _, tx := range txs {
for i, output := range tx.TXOutputs {
if bytes.Equal(senderPubKeyHash, output.PubKeyHash){
//比較
if calc < amount {
//2.把utxo加進來
utxos[string(tx.TXID)] = append(utxos[string(tx.TXID)], uint64(i))
//統計一下當前utxo的總額
calc += output.Value
if calc >= amount {
fmt.Printf("找到了滿足的金額: %f\n", calc)
return utxos, calc
}
} else {
fmt.Printf("不滿足轉賬金額,當前金額:%f, 目標金額:%f\n", calc, amount)
}
}
}
}
return utxos, calc
}
8.3 簽名

func (tx *Transaction) Sign(privateKey *ecdsa.PrivateKey, prevTXs map[string]Transaction){
//對每一個input都簽名一次,簽名的資料是由當前input參考的output的哈希+當前的outputs
if tx.IsCoinbase(){
return
}
//1.創建一個當前交易的副本:txCopy,使用函式:TrimmedCopy:要把Signature和PubKey欄位設定為null
txCopy := tx.TrimmedCopy()
//2.回圈遍歷txCopy的inputs,得到這個input索引的output的公鑰哈希
for i, input := range txCopy.TXInputs{
prevTX := prevTXs[string(input.Txid)]
if len(prevTX.TXID) == 0{
log.Panic("參考的交易無效\n")
}
//設定副本輸入的公鑰
txCopy.TXInputs[i].PubKey = prevTX.TXOutputs[input.Index].PubKeyHash
//3,對拼好的txCopy進行哈希處理,SetHash得到TXID,這個TXID就是要簽名的最終資料
txCopy.SetHash()
//還原,以免影響后面的input簽名
txCopy.TXInputs[i].PubKey = nil
signDataHash := txCopy.TXID
//4.執行簽名動作,得到r,s位元組流
r, s, err := ecdsa.Sign(rand.Reader, privateKey, signDataHash)
if err != nil{
log.Panic(err)
}
//5.放到簽名的inputs的Signature中
signature := append(r.Bytes(), s.Bytes()...)
tx.TXInputs[i].Signature = signature
}
}
8.4 校驗

func (tx *Transaction) Verify (prevTXs map[string]Transaction) bool{
if tx.IsCoinbase(){
return true
}
//1.得到簽名的資料
txCopy := tx.TrimmedCopy()
for i, input := range tx.TXInputs{
prevTX := prevTXs[string(input.Txid)]
if len(prevTX.TXID) == 0{
log.Panic("參考的交易無效\n")
}
txCopy.TXInputs[i].PubKey = prevTX.TXOutputs[input.Index].PubKeyHash
txCopy.SetHash()
dataHash := txCopy.TXID
//2.得到signature,反退回r,s
signature := input.Signature
//3拆解PubKey, X,Y得到原生公鑰
pubKey := input.PubKey
r := big.Int{}
s := big.Int{}
r.SetBytes(signature[:len(signature)/2])
s.SetBytes(signature[len(signature)/2:])
X := big.Int{}
Y := big.Int{}
//pubKey平均分,前半部分給X,后半部分給Y
X.SetBytes(pubKey[:len(pubKey)/2])
Y.SetBytes(pubKey[len(pubKey)/2:])
pubKeyOrigin := ecdsa.PublicKey{elliptic.P256(), &X, &Y}
//4.Verify
if !ecdsa.Verify(&pubKeyOrigin, dataHash, &r, &s){
return false
}
}
return true
}
說明
原始碼來自于黑馬程式員的區塊鏈教程,本篇文章只是簡單的對代碼邏輯進行梳理,具體細節可以查看原始碼,另外,系統中還有很多可以完善的地方,以后有機會的話會繼續完整這個系統,
原始碼地址:https://github.com/intrepidzoro/learn_blockchain/tree/master/go_bitcoin
最后
歡迎大家加入到區塊鏈交流學習群,群里有很多大佬,并且有很多學習資源可以自行下載,祝愿大家都能在區塊鏈領域有所成就,

最后推薦一位大佬的公眾號,歡迎關注哦:區塊鏈技術堆疊
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/274786.html
標籤:區塊鏈
上一篇:C++ yaml決議實戰暨yaml-cpp庫使用(3)yaml-cpp庫生成yaml格式檔案
下一篇:Go常用命令及環境配置
