Flasbots Builder详解 (1)初始化

初始化

Flashbots Builder 是在 geth 基础之上进行开发的，因此大体架构与 geth 保持一致。Builder核心结构定义在builder/builder.go文件中，Builder 对外被封装到 BuilderService 中，对外提供 http 服务。从外至内，先分析 BuilderService，再到 Builder 和其内部组成。

BuilderService

封装了 IBuilder 接口和 httpServer

NewService 参数：传入监听的地址，localRelay 配置，builder 实例

localRelay 不为空，则初始化路由

GET：/

GET: /eth/v1/builder/status

POST: /eth/v1/builder/validators

GET: /eth/v1/builder/header/{slot:[0-9]+}/{parent_hash:0x[a-fA-F0-9]+}/{pubkey:0x[a-fA-F0-9]+}

POST: /eth/v1/builder/blinded_blocks

Start：启动 Http 服务，启动 builder

Stop：关闭 Http 服务，关闭 builder

Builder

首先看NewBuilder的参数BuilderArgs ，对应字段的注释已经添加

Copy
type BuilderArgs struct {
sk *bls.SecretKey // 运行builder的私钥
ds flashbotsextra.IDatabaseService // 本地的数据库服务
relay IRelay // relay接口
builderSigningDomain phase0.Domain // 用于提交区块时签名的域名
builderBlockResubmitInterval time.Duration // 间隔一段时间重复提交区块
discardRevertibleTxOnErr bool // 无用
eth IEthereumService // eth实例，用于构建区块
dryRun bool // 是否需要向relay提交区块
ignoreLatePayloadAttributes bool // 用于在监听事件时过滤
validator *blockvalidation.BlockValidationAPI // dryRun时用于提交验证
beaconClient IBeaconClient // 信标链client，用于监听事件构建payload
submissionOffsetFromEndOfSlot time.Duration // 提交区块的等待时间

limiter *rate.Limiter // 用作限流
}

可以看到 builder 的核心组成包括 ds, relay, eth 和 beaconClient，其他的参数在后面应用到时会做进一步的解释。

Relay

首先 Relay 出现在两处（BuilderService 和 Builder），且有两种类型（localRelay 和 remoteRelay）

在 BuilderService 中已经有的是 localRelay，如果 localRelay 不为空，便会在 BuilderService 中的 http 服务注册相应的路由

在 Builder 中有 Relay，如果没有配置 remoteReplayEndpoint，就用 localRelay，否则是 remoteRelay

由此可见，每个 builder 不一定要自己跑一个 relay 服务，但是一定会连接到相应的 relay 服务

Relay 的接口定义如下所示：

Copy
type IRelay interface {
SubmitBlock(msg *bellatrixapi.SubmitBlockRequest, vd ValidatorData) error
SubmitBlockCapella(msg *capellaapi.SubmitBlockRequest, vd ValidatorData) error
GetValidatorForSlot(nextSlot uint64) (ValidatorData, error)
Config() RelayConfig
Start() error
Stop()
}

DatabaseService

先回到前面初始化 DatabaseService的地方，在初始化时会判断是否配置 postgres 连接，如果没有的话会初始化一个默认的 dbService，否则就建立 db 连接

Copy
var ds flashbotsextra.IDatabaseService
dbDSN := os.Getenv("FLASHBOTS_POSTGRES_DSN")
if dbDSN != "" {
ds, err = flashbotsextra.NewDatabaseService(dbDSN)
if err != nil {
log.Error("could not connect to the DB", "err", err)
ds = flashbotsextra.NilDbService{}
}
} else {
log.Info("db dsn is not provided, starting nil db svc")
ds = flashbotsextra.NilDbService{}
}

IDatabaseService的接口定义如下：对于默认的 NilDbService，都返回的空

Copy
type IDatabaseService interface {
ConsumeBuiltBlock(block *types.Block, blockValue *big.Int, OrdersClosedAt time.Time, sealedAt time.Time,
commitedBundles []types.SimulatedBundle, allBundles []types.SimulatedBundle,
usedSbundles []types.UsedSBundle,
bidTrace *apiv1.BidTrace)
GetPriorityBundles(ctx context.Context, blockNum int64, isHighPrio bool) ([]DbBundle, error)
GetLatestUuidBundles(ctx context.Context, blockNum int64) ([]types.LatestUuidBundle, error)
}

在具体实现中，主要记录的是 blocks bundles 表等信息，用于记录本地 builder 的构建区块和 bundle 信息，也用做查询和替换用，在后面会介绍到

Eth

这里定义的是结构是EthereumService，是对 eth 实例的封装，eth 实例也就是熟悉的 geth 中的 eth 实例，在前面被初始化，这里抽象成IEthereumService 接口，对外提供以下四个方法

Copy
type IEthereumService interface {
BuildBlock(attrs *types.BuilderPayloadAttributes, sealedBlockCallback miner.BlockHookFn) error
GetBlockByHash(hash common.Hash) *types.Block
Config() *params.ChainConfig
Synced() bool
}

BeaconClient

beaconClient 是用来与信标链节点通信的，在初始化部分，根据配置 BeaconEndpoints，选择是NewBeaconClient or NewMultiBeaconClient

Copy
var beaconClient IBeaconClient
if len(cfg.BeaconEndpoints) == 0 {
beaconClient = &NilBeaconClient{}
} else if len(cfg.BeaconEndpoints) == 1 {
beaconClient = NewBeaconClient(cfg.BeaconEndpoints[0], cfg.SlotsInEpoch, cfg.SecondsInSlot)
} else {
beaconClient = NewMultiBeaconClient(cfg.BeaconEndpoints, cfg.SlotsInEpoch, cfg.SecondsInSlot)
}

BeaconClient 包括三个核心的字段：

endpoint ：建立 beaconClient 的连接

slotsInEpoch ：默认 32

secondsInSlot: 默认 12

slot 是 eth2.0 中的配置，是一个固定的时间段，通常是 12s，所有的活跃验证者按照一定规则轮流担当不同 slot 时的 proposer，每个 slot 对应打包一个区块

epoch 是更大的时间周期，由多个连续的 slot 组成，通常为 32 个 slot

对外提供以下几种方法：

Copy
type IBeaconClient interface {
isValidator(pubkey PubkeyHex) bool
getProposerForNextSlot(requestedSlot uint64) (PubkeyHex, error)
SubscribeToPayloadAttributesEvents(payloadAttrC chan types.BuilderPayloadAttributes)
Start() error
Stop()
}

重点的方法就是 SubscribeToPayloadAttributesEvents 用来获取定阅的请求