关于 rollup 向一层提交交易的我们上面文章已经由提到,此处主要主要剖析 rollup 合约,rollup watcher 和欺诈证明之间的交互和细节的逻辑整理。
汇总协议跟踪汇总块链——为了清楚起见,我们将这些称为“RBlocks”。它们与第 1 层以太坊区块不同,也与第 2 层 Nitro 区块不同。您可以将 RBlocks 视为形成一个单独的链,由 Arbitrum 汇总协议管理和监督。
关于汇总块链相关的解释请参考:nitro/inside-arbitrum-nitro.md at master · OffchainLabs/nitro
rollup_watcher 会去监听 rollup 相关的事件,监听的事件如下:
var rollupInitializedID common.Hash
var nodeCreatedID common.Hash
var challengeCreatedID common.Hash
这些事件分别由 LookupCreation LookupNode LookupNodeChildren 和 LookupChallengedNode 等方法处理,L1Validator 将这些调用这里面的方法获取相关的合约事件信息验证。
RollupNode 定义
struct Node {// Hash of the state of the chain as of this nodebytes32 stateHash;// Hash of the data that can be challengedbytes32 challengeHash;// Hash of the data that will be committed if this node is confirmedbytes32 confirmData;// Index of the node previous to this oneuint64 prevNum;// Deadline at which this node can be confirmeduint64 deadlineBlock;// Deadline at which a child of this node can be confirmeduint64 noChildConfirmedBeforeBlock;// Number of stakers staked on this node. This includes real stakers and zombiesuint64 stakerCount;// Number of stakers staked on a child node. This includes real stakers and zombiesuint64 childStakerCount;// This value starts at zero and is set to a value when the first child is created. After that it is constant until the node is destroyed or the owner destroys pending nodesuint64 firstChildBlock;// The number of the latest child of this node to be createduint64 latestChildNumber;// The block number when this node was createduint64 createdAtBlock;// A hash of all the data needed to determine this node's validity, to protect against reorgsbytes32 nodeHash;
}
参加 rollup 的角色:分别由 WatchtowerStrategy,DefensiveStrategy, StakeLatestStrategy 和 MakeNodesStrategy
代码定义如下,该代码位于 staker.go 里面:
const (// Watchtower: don't do anything on L1, but log if there's a bad assertionWatchtowerStrategy StakerStrategy = iota// Defensive: stake if there's a bad assertionDefensiveStrategy// Stake latest: stay staked on the latest node, challenging bad assertionsStakeLatestStrategy// Make nodes: continually create new nodes, challenging bad assertionsMakeNodesStrategy
)
err := s.updateBlockValidatorModuleRoot(ctx)
updateBlockValidatorModuleRoot 里面会去从 rollup 的中取到 WasmModuleRoot 更新到 blockValidator 里面, 当然 l1 validator 初始化的时候也会去更新相应的 WasmModuleRoot,并把 WasmModuleRoot 设置成 blockValidator 当前的 ModuleRoot,以供验证使用。
type CallOpts struct {Pending bool // Whether to operate on the pending state or the last known oneFrom common.Address // Optional the sender address, otherwise the first account is usedBlockNumber *big.Int // Optional the block number on which the call should be performedContext context.Context // Network context to support cancellation and timeouts (nil = no timeout)
}
func (b *ValidatorTxBuilder) ClearTransactions() {b.transactions = nil
}
function latestStaked(IRollupCore rollup, address staker)externalviewreturns (uint64, Node memory)
{uint64 num = rollup.latestStakedNode(staker);if (num == 0) {num = rollup.latestConfirmed();}Node memory node = rollup.getNode(num);return (num, node);
}
function areUnresolvedNodesLinear(IRollupCore rollup) external view returns (bool) {uint256 end = rollup.latestNodeCreated();for (uint64 i = rollup.firstUnresolvedNode(); i <= end; i++) {if (i > 0 && rollup.getNode(i).prevNum != i - 1) {return false;}}return true;
}
nodesLinear, err := s.validatorUtils.AreUnresolvedNodesLinear(callOpts, s.rollupAddress)
if err != nil {return nil, err
}
if !nodesLinear {log.Warn("rollup assertion fork detected")if effectiveStrategy == DefensiveStrategy {effectiveStrategy = StakeLatestStrategy}s.inactiveLastCheckedNode = nil
}
s.bringActiveUntilNode 存储并且 info.LatestStakedNode < s.bringActiveUntilNode, effectiveStrategy 会由 DefensiveStrategy 切换到 StakeLatestStrategy
/// @return Index of the latest confirmed node
function latestConfirmed() public view override returns (uint64) {return _latestConfirmed;
}
const (CONFIRM_TYPE_NONE ConfirmType = iotaCONFIRM_TYPE_VALIDCONFIRM_TYPE_INVALID
)
处理逻辑是先去获取 confirmType 和 unresolvedNodeIndex,分别调用 ValidatorUtils 里面的 checkDecidableNextNode 函数和 rollup 的 firstUnresolvedNode,如果是 CONFIRM_TYPE_INVALID 类别的,直接拒绝下一个节点,如果是 CONFIRM_TYPE_VALID,查找并确认下一个节点。核心逻辑代码如下
func (v *L1Validator) resolveNextNode(ctx context.Context, info *StakerInfo, latestConfirmedNode *uint64) (bool, error) {callOpts := v.getCallOpts(ctx)confirmType, err := v.validatorUtils.CheckDecidableNextNode(callOpts, v.rollupAddress)if err != nil {return false, err}unresolvedNodeIndex, err := v.rollup.FirstUnresolvedNode(callOpts)if err != nil {return false, err}switch ConfirmType(confirmType) {case CONFIRM_TYPE_INVALID:addr := v.wallet.Address()if info == nil || addr == nil || info.LatestStakedNode <= unresolvedNodeIndex {// We aren't an example of someone staked on a competitorreturn false, nil}log.Info("rejecing node", "node", unresolvedNodeIndex)_, err = v.rollup.RejectNextNode(v.builder.Auth(ctx), *addr)return true, errcase CONFIRM_TYPE_VALID:nodeInfo, err := v.rollup.LookupNode(ctx, unresolvedNodeIndex)if err != nil {return false, err}afterGs := nodeInfo.AfterState().GlobalState_, err = v.rollup.ConfirmNextNode(v.builder.Auth(ctx), afterGs.BlockHash, afterGs.SendRoot)if err != nil {return false, err}*latestConfirmedNode = unresolvedNodeIndexreturn true, nildefault:return false, nil}
}
func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy StakerStrategy) error {active := effectiveStrategy >= StakeLatestStrategyaction, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy, s.config.MakeAssertionInterval)if err != nil {return err}if wrongNodesExist && effectiveStrategy == WatchtowerStrategy {log.Error("found incorrect assertion in watchtower mode")}if action == nil {info.CanProgress = falsereturn nil}switch action := action.(type) {case createNodeAction:if wrongNodesExist && s.config.DisableChallenge {log.Error("refusing to challenge assertion as config disables challenges")info.CanProgress = falsereturn nil}if !active {if wrongNodesExist && effectiveStrategy >= DefensiveStrategy {log.Warn("bringing defensive validator online because of incorrect assertion")s.bringActiveUntilNode = info.LatestStakedNode + 1}info.CanProgress = falsereturn nil}// Details are already logged with more details in generateNodeActioninfo.CanProgress = falseinfo.LatestStakedNode = 0info.LatestStakedNodeHash = action.hash// We'll return early if we already havea stakeif info.StakeExists {_, err = s.rollup.StakeOnNewNode(s.builder.Auth(ctx), action.assertion.AsSolidityStruct(), action.hash, action.prevInboxMaxCount)return err}// If we have no stake yet, we'll put one downstakeAmount, err := s.rollup.CurrentRequiredStake(s.getCallOpts(ctx))if err != nil {return err}_, err = s.rollup.NewStakeOnNewNode(s.builder.AuthWithAmount(ctx, stakeAmount),action.assertion.AsSolidityStruct(),action.hash,action.prevInboxMaxCount,)if err != nil {return err}info.StakeExists = truereturn nilcase existingNodeAction:info.LatestStakedNode = action.numberinfo.LatestStakedNodeHash = action.hashif !active {if wrongNodesExist && effectiveStrategy >= DefensiveStrategy {log.Warn("bringing defensive validator online because of incorrect assertion")s.bringActiveUntilNode = action.numberinfo.CanProgress = false} else {s.inactiveLastCheckedNode = &nodeAndHash{id: action.number,hash: action.hash,}}return nil}log.Info("staking on existing node", "node", action.number)// We'll return early if we already havea stakeif info.StakeExists {_, err = s.rollup.StakeOnExistingNode(s.builder.Auth(ctx), action.number, action.hash)return err}// If we have no stake yet, we'll put one downstakeAmount, err := s.rollup.CurrentRequiredStake(s.getCallOpts(ctx))if err != nil {return err}_, err = s.rollup.NewStakeOnExistingNode(s.builder.AuthWithAmount(ctx, stakeAmount),action.number,action.hash,)if err != nil {return err}info.StakeExists = truereturn nildefault:panic("invalid action type")}
}
接下来的流程:createConflict->BuildingTransactionCount->ExecuteTransactions
完整layer2 github 链接:https://github.com/guoshijiang/layer2