您正在查看: 2022年11月

Bittorrent-Chain私链搭建 - BTTC层(一)

部署仅在技术方案调研角度,客观测试,仅作记录
先草稿记录,后续再做整理

现有主网或者测试网

对于现有主网或者测试网节点的加入可参考
https://github.com/bttcprotocol/launch
环境进行运行,注意下脚本默认运行目录在home目录,可以通过执行--home 参数进行相应变更。

私链搭建

一方面为了技术调研,目前需要做的是搭建一个完整的BTTC私链
主要分为三部分进行测试部署

1. BTTC生产层

github: https://github.com/bttcprotocol/bttc.git
对于bttc,是从go-ethereum修改而来,简单来说这一层负责区块的生产
https://doc.bt.io/zh-Hans/docs/basics/bttc-basics/bttc-pos-architecture#bttc-%E5%8C%BA%E5%9D%97%E7%94%9F%E4%BA%A7%E8%80%85%E5%B1%82

2. Delivery层

github: https://github.com/bttcprotocol/delivery
负责区块验证、Bttc层区块生产者的选择、代表Bttc层状态检查点的验证和提交,以及其他各种责任。
https://doc.bt.io/zh-Hans/docs/basics/bttc-basics/bttc-pos-architecture#delivery-pos%E5%B1%82

3. 部署在TRON网络上的质押管理合约

Staking合约

从出块共识来说

简单来说,相对其他go-ethereum修改的pos链,Bittorrent-Chain的选举是在TRON网络上的质押管理合约中的,然后通过Delivery层进行数据的验证者的排名,然后bttc更新验证者排名时从Delivery层获取,作为下次出块周期的验证者列表。

测试私链

根据bttc的三层实现,计划私链的启动顺序如下

  1. 仅bttc层,完成私链搭建
    来验证bttc层genesis参数,初始化周期验证者的选择逻辑,以及一些启动参数的测试
  2. TRON测试网络部署相关合约
    来验证合约的编译和部署过程,为下一步与Delivery层结合做准备
  3. bttc + Delivery层 + TRON网络上的质押管理合约
    整体测试

bttc层私链搭建

先使用官方的测试例子,测一下
https://github.com/bttcprotocol/contracts/tree/stake/test-blockchain
从中找到genesis模板
https://github.com/bttcprotocol/contracts/blob/stake/test-blockchain/genesis.json.template
https://github.com/bttcprotocol/bttc/blob/master/tests/bor/testdata/genesis.json

从确认初始化验证者加入方式,重点关注validatorContract合约,对应地址0x0000000000000000000000000000000000001000
常规的初始化验证者的加入,时添加到extraData或者其他数组数据设置,从demo没看到对用的数据,然后调试整体代码

调试整体代码

miner->commitNewWork->commit->FinalizeAndAssemble->(headerNumber%c.config.Sprint == 0)->checkAndCommitSpan->needToCommitSpan->fetchAndCommitSpan->WithoutHeimdall
getNextHeimdallSpanForTest / HeimdallClient.FetchWithRetry

发现bttc层溜了一个测试开关,

--bor.withoutheimdall

启动参数添加后,进入Test模式,也就是bttc下一周期(genesis配置的Sprint,默认64)的验证者列表会执行getNextHeimdallSpanForTest继续从合约中初始数据中获取

GetCurrentValidators->getBorValidators

修改初始化验证者地址

默认的验证者地址是在getInitialValidators设置的
https://github.com/bttcprotocol/genesis-contracts/blob/63b72f1ca9a20064d4e700b026ca631335717b4b/contracts/BorValidatorSet.sol#L216

  /// Get current validator set (last enacted or initial if no changes ever made) with current stake.
  function getInitialValidators() public view returns (address[] memory, uint256[] memory) {
    address[] memory addrs = new address[](3);
    addrs[0] = 0xfA841eAAcf03598bAadF0266eF6097C654DE5465;
    addrs[1] = 0x80CFb197Be875eE45294AC31406E6483e3eAb02E;
    addrs[2] = 0x0aB3ab4542ED5FA2A67B2B8DAbC82C42162853A6;
    uint256[] memory powers = new uint256[](3);
    powers[0] = 1;
    powers[1] = 1;
    powers[2] = 1;
    return (addrs, powers);
  }

需要修改成我们自己的私链初始化验证者地址

编译系统合约

  1. Clone code

    git clone https://github.com/bttcprotocol/genesis-contracts.git
  2. 修改我们私链的初始化验证者地址
    https://github.com/bttcprotocol/genesis-contracts/blob/master/validators.json

    [
    {
     "address": "0xfA841eAAcf03598bAadF0266eF6097C654DE5465",
     "stake": 1,
     "balance": 300000000
    },
    {
     "address": "0x80CFb197Be875eE45294AC31406E6483e3eAb02E",
     "stake": 1,
     "balance": 300000000
    },
    {
     "address": "0x0aB3ab4542ED5FA2A67B2B8DAbC82C42162853A6",
     "stake": 1,
     "balance": 300000000
    }
    ]
  3. 生成我们私链的合约

    node generate-borvalidatorset.js --bttc-chain-id 9527 --delivery-chain-id delivery-9527
  4. Install dependencies and submodules

    $ npm install
    $ git submodule init
    $ git submodule update
  5. Compile Bttc contracts
    切记!!node需要v11版本!

    nvm install v11
    nvm use v11

    下面truffle:compile依赖docker
    https://desktop.docker.com/mac/main/amd64/Docker.dmg?utm_source=docker&utm_medium=webreferral&utm_campaign=docs-driven-download-mac-amd64

    $ cd bttc-contracts
    $ npm install
    $ node scripts/process-templates.js --bttc-chain-id <bttc-chain-id> // 9527
    $ npm run truffle:compile
    $ cd ..

附加

如果不加开关将会

--bor.heimdall "http://localhost:1317" 

第一轮(sprint 64)验证者 Signer 是从系统合约getBorValidators,获取的固定的信息,
查询此期间block的miner为
0x0000000000000000000000000000000000000000
下一周期开始与delivery层进行数据获取

参考:https://doc.bt.io/zh-Hans/docs/basics/bttc-basics/bttc-pos-architecture

参考

项目官网:https://bt.io/
github: https://github.com/bttcprotocol
官方文档:https://doc.bt.io/zh-Hans/

以太坊一个区块可以包含多少交易

今天在做以太坊开发时,突然想到一个问题,以太坊一个区块包含的交易个数由什么决定?

如果交易池中有足够的交易,一个区块最多可容纳多少交易?

带着这个问题,我阅读了一下go-ethereum中关于挖矿的源代码,找到了答案。

先抛出结论:

影响一个区块交易个数的因素有两个:

  1. 交易池中的交易个数。这个很好理解,交易池的交易个数直接决定了一个矿工可以打包多少交易到区块中。
  2. 区块允许的GasLimit。GasLimit又由父块GasLimit、GasFloor和GasCeil共同决定(1.8版本以前只受父块GasLimit影响),当交易的gas总计大于GasLimit时,交易将不在打包的区块中。
    其中gasFloor和gasCeil是在geth的中mine的两个配置项。详见文档:https://geth.ethereum.org/docs/interface/command-line-options

结论主要从"go-ethereum/miner/worker.go"源文件中得出。worker相当于一个挖矿工人,负责具体的挖矿工作流程。在worker对象中有一个“commitNewWork”方法,是创建一个新的区块,其中计算了该区块的GasLimit和需要处理的交易池中的交易。相关代码如下:

func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) {
    // 创建区块头
    header := &types.Header{
        ParentHash: parent.Hash(),
        Number:     num.Add(num, common.Big1),
        // 计算GasLimit
        GasLimit:   core.CalcGasLimit(parent, w.config.GasFloor, w.config.GasCeil),
        Extra:      w.extra,
        Time:       uint64(timestamp),
    }

    // 从交易池中读取交易
    pending, err := w.eth.TxPool().Pending()
    if err != nil {
        log.Error("Failed to fetch pending transactions", "err", err)
        return
    }

    // Short circuit if there is no available pending transactions
    if len(pending) == 0 {
        w.updateSnapshot()
        return
    }

    // Split the pending transactions into locals and remotes
    localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending
    for _, account := range w.eth.TxPool().Locals() {
        if txs := remoteTxs[account]; len(txs) > 0 {
            delete(remoteTxs, account)
            localTxs[account] = txs
        }
    }

    if len(localTxs) > 0 {
        txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs)
        if w.commitTransactions(txs, w.coinbase, interrupt) {
            return
        }
    }

    if len(remoteTxs) > 0 {
        txs := types.NewTransactionsByPriceAndNonce(w.current.signer, remoteTxs)
        if w.commitTransactions(txs, w.coinbase, interrupt) {
            return
        }
    }
}

“commitNewWork”方法中获取到交易池中的交易后将交易提交给了“commitTransactions”方法对交易进行验证。

func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool {
    // 将区块头中的GasLimit赋值给gasPool
    if w.current.gasPool == nil {
        w.current.gasPool = new(core.GasPool).AddGas(w.current.header.GasLimit)
    }

    // 循环判断所有交易
    for {
        // gasPool中的gas小于21000是跳出循环
        if w.current.gasPool.Gas() < params.TxGas {
            log.Trace("Not enough gas for further transactions", "have", w.current.gasPool, "want", params.TxGas)
            break
        }

        // 按照交易gas从大到小的顺序获取下一个交易
        tx := txs.Peek()

        // 没有交易后跳出循环
        if tx == nil {
            break
        }
        // 提交交易
        logs, err := w.commitTransaction(tx, coinbase)
    }
}

将交易提交给“commitTransaction”方法后,回调用

receipt, _, err := core.ApplyTransaction(w.config, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, vm.Config{})

在ApplyTransaction方法中会将gasPool中的gas值减去该笔交易的gas,当gasPool的gas值小于21000时,剩下的交易将不在打包的该区块中。

回头在来看一下区块中的GasLimit是怎么计算的。

core.CalcGasLimit(parent, w.config.GasFloor, w.config.GasCeil)方法在“go-ethereum/core/block_validator.go”,代码如下:

// CalcGasLimit computes the gas limit of the next block after parent. It aims
// to keep the baseline gas above the provided floor, and increase it towards the
// ceil if the blocks are full. If the ceil is exceeded, it will always decrease
// the gas allowance.
func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
    // contrib = (parentGasUsed * 3 / 2) / 1024
    contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor

    // decay = parentGasLimit / 1024 -1
    decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1

    /*
        strategy: gasLimit of block-to-mine is set based on parent's
        gasUsed value.  if parentGasUsed > parentGasLimit * (2/3) then we
        increase it, otherwise lower it (or leave it unchanged if it's right
        at that usage) the amount increased/decreased depends on how far away
        from parentGasLimit * (2/3) parentGasUsed is.
    */
    limit := parent.GasLimit() - decay + contrib
    if limit < params.MinGasLimit {
        limit = params.MinGasLimit
    }
    // If we're outside our allowed gas range, we try to hone towards them
    if limit < gasFloor {
        limit = parent.GasLimit() + decay
        if limit > gasFloor {
            limit = gasFloor
        }
    } else if limit > gasCeil {
        limit = parent.GasLimit() - decay
        if limit < gasCeil {
            limit = gasCeil
        }
    }
    return limit
}

计算GasLimit的策略很有意思,受父块的GasLimit、挖矿的gas上限gasCeil和gas下限gasFloor三者共同决定。

当父块的交易里的gas总和大于父块GasLimit的2/3时,则增加当前块的GasLimit,反之减少当前块的GasLimit,同时,保证GasLimit的范围在gasFloor和gasCeil之间。

转载:https://www.jianshu.com/p/3588fd52ec0a