open-ethereum-pool以太坊礦池原始碼分析(7)unlocker模組

尹成發表於2018-05-21
# open-ethereum-pool以太坊礦池-unlocker模組

## unlocker模組配置

```json
"unlocker": {
    "enabled": false,
    "poolFee": 1.0,
    "poolFeeAddress": "",
    "donate": true,
    "depth": 120,
    "immatureDepth": 20,
    "keepTxFees": false,
    "interval": "10m",
    "daemon": "http://127.0.0.1:8545",
    "timeout": "10s"
},
```

## BlockUnlocker定義

```go
type BlockUnlocker struct {
    config *UnlockerConfig
    backend *storage.RedisClient
    rpc *rpc.RPCClient
    halt bool
    lastFail error
}
```

## unlocker流程圖



## GetCandidates原理

```go
func (r *RedisClient) GetCandidates(maxHeight int64) ([]*BlockData, error) {
    //ZRANGEBYSCORE eth:blocks:candidates 0 maxHeight WITHSCORES
    option := redis.ZRangeByScore{Min: "0", Max: strconv.FormatInt(maxHeight, 10)}
    cmd := r.client.ZRangeByScoreWithScores(r.formatKey("blocks", "candidates"), option)
    if cmd.Err() != nil {
        return nil, cmd.Err()
    }
    return convertCandidateResults(cmd), nil
}

func convertCandidateResults(raw *redis.ZSliceCmd) []*BlockData {
    var result []*BlockData
    for _, v := range raw.Val() {
        // "nonce:powHash:mixDigest:timestamp:diff:totalShares"
        block := BlockData{}
        block.Height = int64(v.Score)
        block.RoundHeight = block.Height
        fields := strings.Split(v.Member.(string), ":")
        block.Nonce = fields[0]
        block.PowHash = fields[1]
        block.MixDigest = fields[2]
        block.Timestamp, _ = strconv.ParseInt(fields[3], 10, 64)
        block.Difficulty, _ = strconv.ParseInt(fields[4], 10, 64)
        block.TotalShares, _ = strconv.ParseInt(fields[5], 10, 64)
        block.candidateKey = v.Member.(string)
        result = append(result, &block)
    }
    return result
}
```

## writeImmatureBlock原理

```go
//Immature即未成年
func (r *RedisClient) writeImmatureBlock(tx *redis.Multi, block *BlockData) {
    // Redis 2.8.x returns "ERR source and destination objects are the same"
    if block.Height != block.RoundHeight {
        //RENAME eth:shares:candidates:round&RoundHeight:nonce eth:shares:candidates:round&blockHeight:nonce
        tx.Rename(r.formatRound(block.RoundHeight, block.Nonce), r.formatRound(block.Height, block.Nonce))
    }
    
    //Zrem 命令用於移除有序集中的一個或多個成員,不存在的成員將被忽略
    //candidates為候選者
    //ZREM eth:blocks:candidates nonce:powHash:mixDigest:timestamp:diff:totalShares
    tx.ZRem(r.formatKey("blocks", "candidates"), block.candidateKey)
    
    //ZADD eth:blocks:immature block.Height UncleHeight:Orphan:Nonce:serializeHash:Timestamp:Difficulty:TotalShares:Reward
    tx.ZAdd(r.formatKey("blocks", "immature"), redis.Z{Score: float64(block.Height), Member: block.key()})
}

func (b *BlockData) key() string {
    return join(b.UncleHeight, b.Orphan, b.Nonce, b.serializeHash(), b.Timestamp, b.Difficulty, b.TotalShares, b.Reward)
}
```

## WriteImmatureBlock原理

```go
func (r *RedisClient) WriteImmatureBlock(block *BlockData, roundRewards map[string]int64) error {
    tx := r.client.Multi()
    defer tx.Close()

    _, err := tx.Exec(func() error {
        //寫入未成年塊,目的何在?
        //補充unlockPendingBlocks()階段,均寫入未成年塊
        r.writeImmatureBlock(tx, block)
        total := int64(0)
        //遍歷roundRewards
        for login, amount := range roundRewards {
            //求和
            total += amount
            //Hincrby 命令用於為雜湊表中的欄位值加上指定增量值
            //HINCRBY eth:miners:login immature amount
            tx.HIncrBy(r.formatKey("miners", login), "immature", amount)
            
            //HSETNX eth:credits:immature:Height:Hash login amount
            //Hsetnx 命令用於為雜湊表中不存在的的欄位賦值
            tx.HSetNX(r.formatKey("credits", "immature", block.Height, block.Hash), login, strconv.FormatInt(amount, 10))
        }
        
        //Hincrby 命令用於為雜湊表中的欄位值加上指定增量值
        //HINCRBY eth:finances:immature total
        tx.HIncrBy(r.formatKey("finances"), "immature", total)
        return nil
    })
    return err
}
```

## GetImmatureBlocks原理

```go
func (r *RedisClient) GetImmatureBlocks(maxHeight int64) ([]*BlockData, error) {
    option := redis.ZRangeByScore{Min: "0", Max: strconv.FormatInt(maxHeight, 10)}
    cmd := r.client.ZRangeByScoreWithScores(r.formatKey("blocks", "immature"), option)
    if cmd.Err() != nil {
        return nil, cmd.Err()
    }
    return convertBlockResults(cmd), nil
}

func convertBlockResults(rows ...*redis.ZSliceCmd) []*BlockData {
    var result []*BlockData
    for _, row := range rows {
        for _, v := range row.Val() {
            // "uncleHeight:orphan:nonce:blockHash:timestamp:diff:totalShares:rewardInWei"
            block := BlockData{}
            block.Height = int64(v.Score)
            block.RoundHeight = block.Height
            fields := strings.Split(v.Member.(string), ":")
            block.UncleHeight, _ = strconv.ParseInt(fields[0], 10, 64)
            block.Uncle = block.UncleHeight > 0
            block.Orphan, _ = strconv.ParseBool(fields[1])
            block.Nonce = fields[2]
            block.Hash = fields[3]
            block.Timestamp, _ = strconv.ParseInt(fields[4], 10, 64)
            block.Difficulty, _ = strconv.ParseInt(fields[5], 10, 64)
            block.TotalShares, _ = strconv.ParseInt(fields[6], 10, 64)
            block.RewardString = fields[7]
            block.ImmatureReward = fields[7]
            block.immatureKey = v.Member.(string)
            result = append(result, &block)
        }
    }
    return result
}
```

## WriteOrphan原理

```
func (r *RedisClient) WriteOrphan(block *BlockData) error {
    creditKey := r.formatKey("credits", "immature", block.RoundHeight, block.Hash)
    tx, err := r.client.Watch(creditKey)
    // Must decrement immatures using existing log entry
    immatureCredits := tx.HGetAllMap(creditKey)
    if err != nil {
        return err
    }
    defer tx.Close()

    _, err = tx.Exec(func() error {
        r.writeMaturedBlock(tx, block)

        // Decrement immature balances
        totalImmature := int64(0)
        for login, amountString := range immatureCredits.Val() {
            amount, _ := strconv.ParseInt(amountString, 10, 64)
            totalImmature += amount
            tx.HIncrBy(r.formatKey("miners", login), "immature", (amount * -1))
        }
        tx.Del(creditKey)
        tx.HIncrBy(r.formatKey("finances"), "immature", (totalImmature * -1))
        return nil
    })
    return err
}

func (r *RedisClient) writeMaturedBlock(tx *redis.Multi, block *BlockData) {
tx.Del(r.formatRound(block.RoundHeight, block.Nonce))
tx.ZRem(r.formatKey("blocks", "immature"), block.immatureKey)
tx.ZAdd(r.formatKey("blocks", "matured"), redis.Z{Score: float64(block.Height), Member: block.key()})
}
```

## 參考文件

* [以太坊中的叔塊(uncle block)](http://blog.csdn.net/superswords/article/details/76445278)






網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN




網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN

相關文章