專案 GitHub 位置: https://github.com/weiawesome/block-chain_go
基本上開始進入 對於區塊中計算的環節
首先要面對的便是 交易的處理
交易從進到節點 到交易進去區塊計算的細節為何
這便是今日實現的重點
因此此篇根據以下幾點進行敘述
完成的範圍便是 接受驗證交易 與 刷新區塊
接受驗證交易
Channel 名稱 | Channel 類型 | 地點 |
---|---|---|
TransactionChannel | 進 | 廣播系統 |
BroadcastTransactionChannel | 出 | 廣播系統 |
BlockTransactionChannel | 出 | 刷新區塊 |
刷新區塊
Channel 名稱 | Channel 類型 | 地點 |
---|---|---|
BlockTransactionChannel | 進 | 接受驗證交易 |
CompleteBlockChannel | 進 | 刷新資料庫 |
MinersBlockChannel | 出 | 礦工管理者 |
檔案位置: "./service/receive_validate_transaction/receive_validate_transaction.go"
func ReceiveValidateTransaction(TransactionChannel chan transaction.Transaction, BroadcastTransactionChannel chan transaction.Transaction, BlockTransactionChannel chan blockchain.BlockTransaction) {
for {
select {
case t := <-TransactionChannel:
block, err := block_control.GetLastBlock()
if err != nil {
continue
}
if blockdb.TransactionInCheckedBlocks(block, t.TransactionHash) {
StringValue, err := t.ToString()
if err != nil {
continue
}
key, err := utils.DecodePublicKey(t.PublicKey)
if err != nil {
continue
}
address := utils.GetAddress(conseous.VersionPrefix, key)
if utils.Verify(t.Signature, key, StringValue) {
sumOfHave := float64(0)
flag := false
for _, from := range t.From {
value, err := utxo.GetUTXO(from.UTXOHash, from.Index)
if err != nil || value.Spent == true {
continue
}
if address != value.Address {
flag = true
break
}
sumOfHave += value.Amount
}
if flag {
continue
}
sumOfSpent := float64(0)
for _, to := range t.To {
sumOfSpent += to.Amount
}
sumOfSpent += t.Fee
if sumOfSpent > sumOfHave {
continue
}
BroadcastTransactionChannel <- t
BlockTransactionChannel <- transactionUtils.ConvertTransaction(t)
}
}
default:
continue
}
}
}
至於公私鑰相關 簽名驗證方法
檔案位置: "./utils/ecc.go"
type SignatureData struct {
R *big.Int `json:"r"`
S *big.Int `json:"s"`
}
type KeyPair struct {
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
}
func GenerateKeyPair() (KeyPair, error) {
curve := elliptic.P256()
privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return KeyPair{}, err
}
privateKeyBytes := privateKey.D.Bytes()
privateKeyBase64 := base64.StdEncoding.EncodeToString(privateKeyBytes)
publicKey := &privateKey.PublicKey
publicKeyBytes := elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y)
publicKeyBase64 := base64.StdEncoding.EncodeToString(publicKeyBytes)
return KeyPair{PublicKey: publicKeyBase64, PrivateKey: privateKeyBase64}, err
}
func DecodePublicKey(PublicKey string) (*ecdsa.PublicKey, error) {
curve := elliptic.P256()
decodedPublicKeyBytes, err := base64.StdEncoding.DecodeString(PublicKey)
if err != nil {
return &ecdsa.PublicKey{}, err
}
decodedPublicKey := new(ecdsa.PublicKey)
decodedPublicKey.Curve = curve
decodedPublicKey.X, decodedPublicKey.Y = elliptic.Unmarshal(curve, decodedPublicKeyBytes)
return decodedPublicKey, nil
}
func DecodePrivateKey(PrivateKey string) (*ecdsa.PrivateKey, error) {
curve := elliptic.P256()
decodedPrivateKeyBytes, err := base64.StdEncoding.DecodeString(PrivateKey)
if err != nil {
return &ecdsa.PrivateKey{}, err
}
decodedPrivateKey := new(ecdsa.PrivateKey)
decodedPrivateKey.Curve = curve
decodedPrivateKey.D = new(big.Int).SetBytes(decodedPrivateKeyBytes)
return decodedPrivateKey, nil
}
func Signature(Content string, PrivateKey *ecdsa.PrivateKey) (string, error) {
ContentBytes := []byte(Content)
r, s, err := ecdsa.Sign(rand.Reader, PrivateKey, ContentBytes)
if err != nil {
return "", err
}
jsonData, err := json.Marshal(SignatureData{R: r, S: s})
if err != nil {
return "", err
}
return string(jsonData), nil
}
func Verify(Signature string, PublicKey *ecdsa.PublicKey, Content string) bool {
ContentBytes := []byte(Content)
var signature SignatureData
err := json.Unmarshal([]byte(Signature), &signature)
if err != nil {
return false
}
valid := ecdsa.Verify(PublicKey, ContentBytes, signature.R, signature.S)
return valid
}
包含生成、解碼、簽名、驗證 各種方法都有
首先 何為交易池
基本上 就是一個優先佇列(Priority Queue)
當一筆交易的手續費越高
則處理的優先順序越高
檔案位置: "./utils/priority_queue.go"
type PriorityQueue []*blockchain.BlockTransaction
func (pq *PriorityQueue) Len() int { return len(*pq) }
func (pq *PriorityQueue) Less(i, j int) bool {
return (*pq)[i].Fee < (*pq)[j].Fee
}
func (pq *PriorityQueue) Swap(i, j int) {
(*pq)[i], (*pq)[j] = (*pq)[j], (*pq)[i]
}
func (pq *PriorityQueue) Push(x interface{}) {
item := x.(*blockchain.BlockTransaction)
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}
如此一來 當確認過的交易 先丟進優先佇列當中
並逐漸挑選出 手續費較高的交易
檔案位置: "./protocal/refresh_block/refresh_block.go"
func RefreshBlock(BlockTransactionChannel chan blockchain.BlockTransaction, MinersBlockChannel chan blockchain.Block, CompleteBlockChannel chan blockchain.Block) {
TransactionPool := make(utils.PriorityQueue, 0)
var TargetBlock blockchain.Block
key, err := utils.DecodePublicKey(utils.GetPublicKey())
if err != nil {
return
}
address := utils.GetAddress(conseous.VersionPrefix, key)
minerTransaction, err := transactionUtils.MinerTransaction(address)
if err != nil {
return
}
for {
select {
case bt := <-BlockTransactionChannel:
heap.Push(&TransactionPool, bt)
var tmpBlock blockchain.Block
tmpBlock.BlockTransactions = append(tmpBlock.BlockTransactions, minerTransaction)
tmpTransactionPool := TransactionPool
for tmpTransactionPool.Len() > 0 {
t := heap.Pop(&tmpTransactionPool).(*blockchain.BlockTransaction)
tmpBlock.BlockTransactions = append(tmpBlock.BlockTransactions, *t)
if unsafe.Sizeof(tmpBlock) > conseous.BlockSize {
tmpBlock.BlockTransactions = tmpBlock.BlockTransactions[:len(tmpBlock.BlockTransactions)-1]
break
}
}
tmpBlock.ComputeMarkleRoot()
if tmpBlock.BlockTop.MarkleRoot != TargetBlock.BlockTop.MarkleRoot {
TargetBlock = tmpBlock
TargetBlock.BlockTop.Version = conseous.Version
TargetBlock.BlockTop.TimeStamp = time.Now().Unix()
blockHash, err := block_control.GetLastBlock()
if err != nil {
blockHash = conseous.GenesisBlockPreviousHash
TargetBlock.BlockMeta = blockchain.BlockMeta{Content: conseous.GenesisBlockContent}
TargetBlock.BlockTop.BlockHeight = conseous.GenesisBlockHeight
TargetBlock.BlockTop.Difficulty = conseous.GenesisBlockDifficulty
} else {
b, err := block.GetBlock(blockHash)
if err != nil {
continue
}
TargetBlock.BlockTop.BlockHeight = b.BlockTop.BlockHeight + 1
TargetBlock.BlockTop.Difficulty = b.BlockTop.Difficulty
if TargetBlock.BlockTop.BlockHeight%conseous.DifficultyCycle == 0 {
speed, err := block.CheckGenerateSpeed(TargetBlock.BlockTop.BlockHeight)
if err == nil {
if speed {
if TargetBlock.BlockTop.Difficulty > conseous.DifficultyLower {
TargetBlock.BlockTop.Difficulty -= 1
}
} else {
if TargetBlock.BlockTop.Difficulty < conseous.DifficultyUpper {
TargetBlock.BlockTop.Difficulty += 1
}
}
}
}
}
TargetBlock.BlockTop.PreviousHash = blockHash
MinersBlockChannel <- TargetBlock
}
case cb := <-CompleteBlockChannel:
continue
default:
continue
}
}
}
目前CompleteBlockChannel部分內容先省略
計算礦工交易時必須先知道 礦工的地址
而礦工的地址與公鑰息息相關
檔案位置: "./utils/address.go"
func GetAddress(networkPrefix byte, publicKey *ecdsa.PublicKey) string {
publicKeyHash := publicKeyHash(publicKey)
payload := append([]byte{networkPrefix}, publicKeyHash...)
checksum := checksum(payload)
payload = append(payload, checksum...)
address := base58Encode(payload)
return address
}
func publicKeyHash(publicKey *ecdsa.PublicKey) []byte {
pubKeyBytes := elliptic.Marshal(publicKey.Curve, publicKey.X, publicKey.Y)
sha256Hash := sha256.Sum256(pubKeyBytes)
h := ripemd160.New()
h.Write(sha256Hash[:])
publicKeyHash := h.Sum(nil)
return publicKeyHash
}
func checksum(payload []byte) []byte {
firstSHA := sha256.Sum256(payload)
secondSHA := sha256.Sum256(firstSHA[:])
return secondSHA[:4]
}
func base58Encode(input []byte) string {
base58Alphabet := "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
var result []byte
x := new(big.Int).SetBytes(input)
base := big.NewInt(58)
zero := big.NewInt(0)
for x.Cmp(zero) > 0 {
mod := new(big.Int)
x.DivMod(x, base, mod)
result = append([]byte{base58Alphabet[mod.Int64()]}, result...)
}
for _, b := range input {
if b == 0x00 {
result = append([]byte{base58Alphabet[0]}, result...)
} else {
break
}
}
return string(result)
}
公鑰轉換地址步驟
當中在設定時區塊難度時 可以根據平均區塊計算時間去變化
那基本上也屬於區塊鏈協定的一環 不同的區塊鏈規定都不同
在此處可以將相關設定參數 都設定好
檔案位置: "./protocal/conseous/parameters.go"
const (
BlockChecked = 6
DifficultyCycle = 120
AverageBlockGenerateTime = 60 * 30
Version = 1
BlockSize = 10 * 1024 * 1024
GenesisBlockPreviousHash = ""
GenesisBlockContent = "This is the block-chain made by Tcweeei!"
GenesisBlockHeight = 0
GenesisBlockDifficulty = 1
VersionPrefix = 0x00
DifficultyLower = 0
DifficultyUpper = 256
MineEmpty = false
)
包含各種基本設定的資訊
基本上 對於交易進來的順序應該很清晰了
交易會遇到甚麼關卡檢驗 甚麼確認
而當中交易池的概念也很重要
(畢竟工作效率還是高一點好、拿工資(手續費)高的交易做)
希望透過這篇能夠理解
馬不停蹄的繼續前進~~~
衝阿!!! 處理完交易 下個步驟當然是 區塊