以太坊学习备忘录-Clique

共识算法选择

eth/ethconfig/config.go CreateConsensusEngine 209

func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine {
    // If proof-of-authority is requested, set it up
    if chainConfig.Clique != nil {
        return clique.New(chainConfig.Clique, db)
    }
  ...
}

type CliqueConfig struct {
    Period uint64 `json:"period"` // Number of seconds between blocks to enforce  出块间隔, --dev.period
    Epoch  uint64 `json:"epoch"`  // Epoch length to reset votes and checkpoint   选举时间间隔
}

投票列表信息

consensus/clique/clique.go Prepare 506

  if number%c.config.Epoch == 0 {
    for _, signer := range snap.signers() {
      header.Extra = append(header.Extra, signer[:]...)
    }
  }

dev 模式下面,创世块有一个投票人

投票人信息

保留在header extraData数据中。可以使用密码学得方式解码出地址。

出块机会

consensus/clique/clique.go Seal 599
防止有人作恶。

    for seen, recent := range snap.Recents {
        if recent == signer {
            // Signer is among recents, only wait if the current block doesn't shift it out
            if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit {
                log.Info("Signed recently, must wait for others")
                return nil
            }
        }
    }

number < limit:人很多,你出了就暂时不要出块了。seen > number-limit:你最近不是已经出过快了吗?让别人出。

经过上诉步骤还是有多个人出块? 如果不是inturn,随机延迟一段时间出块。允许并发出块。给 noturn 加延迟时间的方式来支持 inturn 首先出块,避免 noturn 的结点无谓生成区块。

    delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple
    if header.Difficulty.Cmp(diffNoTurn) == 0 {
        // It's not our turn explicitly to sign, delay it a bit
        wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
        delay += time.Duration(rand.Int63n(int64(wiggle)))

        log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
    }

确定出块人

此数值结果反映在区块难度里面。

    diffInTurn = big.NewInt(2) // Block difficulty for in-turn signatures     轮到我出块了
    diffNoTurn = big.NewInt(1) // Block difficulty for out-of-turn signatures 不是我出块

func (s *Snapshot) inturn(number uint64, signer common.Address) bool {
    signers, offset := s.signers(), 0
    for offset < len(signers) && signers[offset] != signer {
        offset++
    }
    return (number % uint64(len(signers))) == uint64(offset)
}

动态调整出块人

启动得时候增加clique --http.api eth,...,clique console,输入clique即可看到如下方法

{
  proposals: {},
  discard: function(),
  getProposals: function(callback),
  getSigners: function(),
  getSignersAtHash: function(),
  getSnapshot: function(),
  getSnapshotAtHash: function(),
  propose: function(),
  status: function()
}

clique.propose("0x5e17555c1ed3bb5010aa9386d56ca823a0f33d19", true/false);

对应RPC的go代码 consensus/clique/api.go

对应的go代码 consensus/clique/snapshot.go apply 185

大概逻辑就是:票数过半,如果是赞成加入,那就加入出块人列表。如果是反对,那就踢出出块列表。

        if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 {
            if tally.Authorize {
                snap.Signers[header.Coinbase] = struct{}{}
            } else {

            }
        }

每隔 blockNumber % (checkpointInterval = 1024) == 0 个区块会将snap写入数据库保存。

数据签名

accounts/keystore/wallet.go 97

func (w *keystoreWallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
    return w.signHash(account, crypto.Keccak256(data))
}

搭建一个基于PoA的四节点

创建挖矿账号

使用命令geth --datadir node1 account new依次创建需要挖矿的账号,想要多少个节点就创建多少个节点。

创建genesis.json文件

使用交互式命令puppeth,根据提示创建一个genesis.json文件。创建出来的genesis.json文件内容类似如下:

{
  "config": {
    "chainId": 1205,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "clique": {
      "period": 15,
      "epoch": 30000
    }
  },
  "nonce": "0x0",
  "timestamp": "0x60de826f",
  "extraData": "0x000000000000000000000000000000000000000000000000000000000000000023893ab711b8a31f166e5977191a9acb737d49089ae5c0f24355ca814c0c99d7ee9637ec005fae18b3b1a10f515740ecf805c31c5c612935d60ba5eb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "gasLimit": "0x47b7600000",
  "difficulty": "0x1",
  "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x0000000000000000000000000000000000000000",
  "alloc": {
    "23893ab711b8a31f166e5977191a9acb737d4908": {
      "balance": "0x200000000000000000000000000000000000000000000000000000000000000"
    },
    "9ae5c0f24355ca814c0c99d7ee9637ec005fae18": {
      "balance": "0x200000000000000000000000000000000000000000000000000000000000000"
    },
    "b3b1a10f515740ecf805c31c5c612935d60ba5eb": {
      "balance": "0x200000000000000000000000000000000000000000000000000000000000000"
    }
  },
  "number": "0x0",
  "gasUsed": "0x0",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "baseFeePerGas": null
}

其中extraData里面有初始的出块人列表。alloc里面是默认账号的初始金额。

创建创世块

使用命令geth --datadir node1 init genesis.json依次创建创世块

启动节点

使用命令./geth --datadir ./node1 --ipcdisable --port 30001 --nodiscover --http --http.corsdomain "*" --http.addr 0.0.0.0 --http.port 41231 --http.api eth,net,web3,personal,admin,miner,txpool,clique --log.debug --verbosity 5 --allow-insecure-unlock console 启动链。参数根据自己需求进行增删。

解锁账号并开启挖矿

节点互连

由于启动的节点使用了 --nodiscover 参数。所以启动之后还没有链接起来,可以使用 RPC 接口 admin_addPeer 将节点互联起来。每次启动这么弄都很麻烦。可以在节点的数据库里面放一个 static-nodes.json 文件,跟目录 geth 同级即可。 static-nodes.json 文件内容大概如下:

[
    "enode://4d87f6cd87680adb4b51a80ed6bc7125640a29a953b7fd9a3e4ab21009a99436faa16bbb359994bcc9b89e52869abd3b0dc3908e2a368f49eb553609276836b2@127.0.0.1:30001",
    "enode://a3c958413831b2561273b6d2cc21c818e2675443872ba2077a66264d253d7525eb5b895647c37d3249439608506eb0fb99987fe0fbac1c174acb2bf996e658dc@127.0.0.1:30002",
    "enode://d581f48b7c4902b91f9709b7fa8149d10a5f11a1078e196f0d4bdaf89e4b1be43b7c5115e61976384ea96a2dd6f7ca96e500e571221302094c0b18b31caddea6@127.0.0.1:30003",
    "enode://430b1342533e9f0db719bd0eedd46cf5f4b163786e7a0238afccce771869a16604eb67106d288a152b5c814b7fb4cd184ac5cf330761a8f6e624fe2e554eb89c@127.0.0.1:30004"
]

数据量对智能合约的影响

暂无评论

发送评论 编辑评论


				
上一篇
下一篇