本文主要分析Matic数据流在 Ethereum <---> Heimdall <---> Bor
中的数据流动过程。
From Ethereum to polygon
Ethereum contracts
触发depositERC20
合约函数 :
function depositERC20(address _token, uint256 _amount) external {
depositERC20ForUser(_token, msg.sender, _amount);
}
调用syncState
函数:
contract StateSender {
/**
* Emits `stateSynced` events to start sync process on Ethereum chain
* @param receiver Target contract on Bor chain
* @param data Data to send
*/
function syncState (
address receiver,
bytes calldata data
) external;
}
发出 StateSynced
事件。
/**
* Emits `stateSynced` events to start sync process on Ethereum chain
* @param id State id
* @param contractAddress Target contract address on Bor
* @param data Data to send to Bor chain for Target contract address
*/
event StateSynced (
uint256 indexed id,
address indexed contractAddress,
bytes data
);
Heimdall
初始化 clerk processor
// initialize clerk processor
clerkProcessor := NewClerkProcessor(&contractCaller.StateSenderABI)
clerkProcessor.BaseProcessor = *NewBaseProcessor(cdc, queueConnector, httpClient, txBroadcaster, "clerk", clerkProcessor)
注册监听任务:
// RegisterTasks - Registers clerk related tasks with machinery
func (cp *ClerkProcessor) RegisterTasks() {
cp.Logger.Info("Registering clerk tasks")
if err := cp.queueConnector.Server.RegisterTask("sendStateSyncedToHeimdall", cp.sendStateSyncedToHeimdall); err != nil {
cp.Logger.Error("RegisterTasks | sendStateSyncedToHeimdall", "error", err)
}
}
监听收到的日志消息,签名以交易形式广播
消息类型为:
// MsgEventRecord - state msg
type MsgEventRecord struct {
From types.HeimdallAddress `json:"from"`
TxHash types.HeimdallHash `json:"tx_hash"`
LogIndex uint64 `json:"log_index"`
BlockNumber uint64 `json:"block_number"`
ContractAddress types.HeimdallAddress `json:"contract_address"`
Data types.HexBytes `json:"data"`
ID uint64 `json:"id"`
ChainID string `json:"bor_chain_id"`
}
然后将签名消息进行广播:
// HandleStateSyncEvent - handle state sync event from rootchain
// 1. check if this deposit event has to be broadcasted to heimdall
// 2. create and broadcast record transaction to heimdall
func (cp *ClerkProcessor) sendStateSyncedToHeimdall(eventName string, logBytes string) error {
// 判断deposit交易是否已经被处理
if isOld, _ := cp.isOldTx(cp.cliCtx, vLog.TxHash.String(), uint64(vLog.Index)); isOld {
cp.Logger.Info("Ignoring task to send deposit to heimdall as already processed",
"event", eventName,
"id", event.Id,
"contract", event.ContractAddress,
"data", hex.EncodeToString(event.Data),
"borChainId", chainParams.BorChainID,
"txHash", hmTypes.BytesToHeimdallHash(vLog.TxHash.Bytes()),
"logIndex", uint64(vLog.Index),
"blockNumber", vLog.BlockNumber,
)
return nil
}
// return broadcast to heimdall
if err := cp.txBroadcaster.BroadcastToHeimdall(msg); err != nil {
cp.Logger.Error("Error while broadcasting clerk Record to heimdall", "error", err)
return err
}
}
交易处理存储
func PostHandleMsgEventRecord(ctx sdk.Context, k Keeper, msg types.MsgEventRecord, sideTxResult abci.SideTxResultType) sdk.Result {
...
// save event into state
if err := k.SetEventRecord(ctx, record); err != nil {
k.Logger(ctx).Error("Unable to update event record", "error", err, "id", msg.ID)
return types.ErrEventUpdate(k.Codespace()).Result()
}
...
}
提供查询函数:
func handleQueryRecord(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
var params types.QueryRecordParams
if err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms); err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}
// get state record by record id
record, err := keeper.GetEventRecord(ctx, params.RecordID)
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not get state record", err.Error()))
}
// json record
bz, err := json.Marshal(record)
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error()))
}
return bz, nil
}
Bor
Bor 查询获取记录的消息
// CommitStates commit states
func (c *Bor) CommitStates(
state *state.StateDB,
header *types.Header,
chain chainContext,
) ([]*types.StateSyncData, error) {
stateSyncs := make([]*types.StateSyncData, 0)
number := header.Number.Uint64()
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
if err != nil {
return nil, err
}
to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.Sprint).Time), 0)
lastStateID := _lastStateID.Uint64()
log.Info(
"Fetching state updates from Heimdall",
"fromID", lastStateID+1,
"to", to.Format(time.RFC3339))
eventRecords, err := c.HeimdallClient.FetchStateSyncEvents(lastStateID+1, to.Unix()) //查询事件
if c.config.OverrideStateSyncRecords != nil {
if val, ok := c.config.OverrideStateSyncRecords[strconv.FormatUint(number, 10)]; ok {
eventRecords = eventRecords[0:val]
}
}
chainID := c.chainConfig.ChainID.String()
for _, eventRecord := range eventRecords {
if eventRecord.ID <= lastStateID {
continue
}
if err := validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
log.Error(err.Error())
break
}
stateData := types.StateSyncData{
ID: eventRecord.ID,
Contract: eventRecord.Contract,
Data: hex.EncodeToString(eventRecord.Data),
TxHash: eventRecord.TxHash,
}
stateSyncs = append(stateSyncs, &stateData)
// 调用系统合约
if err := c.GenesisContractsClient.CommitState(eventRecord, state, header, chain); err != nil {
return nil, err
}
lastStateID++
}
return stateSyncs, nil
}
查询Heimdall
的接口:
func (h *HeimdallClient) FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error) {
eventRecords := make([]*EventRecordWithTime, 0)
for {
queryParams := fmt.Sprintf("from-id=%d&to-time=%d&limit=%d", fromID, to, stateFetchLimit)
log.Info("Fetching state sync events", "queryParams", queryParams)
response, err := h.FetchWithRetry("clerk/event-record/list", queryParams)
if err != nil {
return nil, err
}
var _eventRecords []*EventRecordWithTime
if response.Result == nil { // status 204
break
}
if err := json.Unmarshal(response.Result, &_eventRecords); err != nil {
return nil, err
}
eventRecords = append(eventRecords, _eventRecords...)
if len(_eventRecords) < stateFetchLimit {
break
}
fromID += uint64(stateFetchLimit)
}
sort.SliceStable(eventRecords, func(i, j int) bool {
return eventRecords[i].ID < eventRecords[j].ID
})
return eventRecords, nil
}
调用系统合约:
func (gc *GenesisContractsClient) CommitState(
event *EventRecordWithTime,
state *state.StateDB,
header *types.Header,
chCtx chainContext,
) error {
eventRecord := event.BuildEventRecord()
recordBytes, err := rlp.EncodeToBytes(eventRecord)
if err != nil {
return err
}
method := "commitState"
t := event.Time.Unix()
data, err := gc.stateReceiverABI.Pack(method, big.NewInt(0).SetInt64(t), recordBytes)
if err != nil {
log.Error("Unable to pack tx for commitState", "error", err)
return err
}
log.Info("→ committing new state", "eventRecord", event.String())
msg := getSystemMessage(common.HexToAddress(gc.StateReceiverContract), data)
if err := applyMessage(msg, state, header, gc.chainConfig, chCtx); err != nil {
return err
}
return nil
}
最终调用ChildERC20
中的deposit
函数。
function deposit(address user, bytes calldata depositData)
external
override
only(DEPOSITOR_ROLE)
{
uint256 amount = abi.decode(depositData, (uint256));
_mint(user, amount);
}
From Polygon to Ethereum
Bor
用户在Bor链上调用withdraw
函数,发起取款交易。
/**
* @notice called when user wants to withdraw tokens back to root chain
* @dev Should burn user's tokens. This transaction will be verified when exiting on root chain
* @param amount amount of tokens to withdraw
*/
function withdraw(uint256 amount) external {
_burn(_msgSender(), amount);
}
Bor 提供获取checkpoint RootHash的API接口:
// GetRootHash returns the merkle root of the start to end block headers
func (api *API) GetRootHash(start uint64, end uint64) (string, error) {
...
length := uint64(end - start + 1)
if length > MaxCheckpointLength {
return "", &MaxCheckpointLengthExceededError{start, end}
}
headers := make([][32]byte, nextPowerOfTwo(length))
for i := 0; i < len(blockHeaders); i++ {
blockHeader := blockHeaders[i]
header := crypto.Keccak256(appendBytes32(
blockHeader.Number.Bytes(),
new(big.Int).SetUint64(blockHeader.Time).Bytes(),
blockHeader.TxHash.Bytes(),
blockHeader.ReceiptHash.Bytes(),
))
var arr [32]byte
copy(arr[:], header)
headers[i] = arr
}
tree := merkle.NewTreeWithOpts(merkle.TreeOptions{EnableHashSorting: false, DisableHashLeaves: true})
if err := tree.Generate(convert(headers), sha3.NewLegacyKeccak256()); err != nil {
return "", err
}
root := hex.EncodeToString(tree.Root().Hash)
api.rootHashCache.Add(key, root)
return root, nil
}
Heimdall
checkpoing
模块有三个任务:
// RegisterTasks - Registers checkpoint related tasks with machinery
func (cp *CheckpointProcessor) RegisterTasks() {
cp.Logger.Info("Registering checkpoint tasks")
if err := cp.queueConnector.Server.RegisterTask("sendCheckpointToHeimdall", cp.sendCheckpointToHeimdall); err != nil {
cp.Logger.Error("RegisterTasks | sendCheckpointToHeimdall", "error", err)
}
if err := cp.queueConnector.Server.RegisterTask("sendCheckpointToRootchain", cp.sendCheckpointToRootchain); err != nil {
cp.Logger.Error("RegisterTasks | sendCheckpointToRootchain", "error", err)
}
if err := cp.queueConnector.Server.RegisterTask("sendCheckpointAckToHeimdall", cp.sendCheckpointAckToHeimdall); err != nil {
cp.Logger.Error("RegisterTasks | sendCheckpointAckToHeimdall", "error", err)
}
}
sendCheckpointToHeimdall
验证者首先判断自己是否为proposer
, 若是的话,检查是否需要生成checkpoint (验证bor最新固化的块和以太坊上最新固化的块数差值是256的倍数), 然后生成checkpoint交易。
// sendCheckpointToHeimdall - handles headerblock from maticchain
// 1. check if i am the proposer for next checkpoint
// 2. check if checkpoint has to be proposed for given headerblock
// 3. if so, propose checkpoint to heimdall.
func (cp *CheckpointProcessor) sendCheckpointToHeimdall(headerBlockStr string) (err error) {
var isProposer bool
if isProposer, err = util.IsProposer(cp.cliCtx); err != nil {
cp.Logger.Error("Error checking isProposer in HeaderBlock handler", "error", err)
return err
}
if isProposer {
// fetch checkpoint context
checkpointContext, err := cp.getCheckpointContext()
if err != nil {
return err
}
// process latest confirmed child block only
chainmanagerParams := checkpointContext.ChainmanagerParams
cp.Logger.Debug("no of checkpoint confirmations required", "maticchainTxConfirmations", chainmanagerParams.MaticchainTxConfirmations)
//获取最新固化的块
latestConfirmedChildBlock := header.Number.Uint64() - chainmanagerParams.MaticchainTxConfirmations
if latestConfirmedChildBlock <= 0 {
cp.Logger.Error("no of blocks on childchain is less than confirmations required", "childChainBlocks", header.Number.Uint64(), "confirmationsRequired", chainmanagerParams.MaticchainTxConfirmations)
return errors.New("no of blocks on childchain is less than confirmations required")
}
expectedCheckpointState, err := cp.nextExpectedCheckpoint(checkpointContext, latestConfirmedChildBlock)
if err != nil {
cp.Logger.Error("Error while calculate next expected checkpoint", "error", err)
return err
}
start := expectedCheckpointState.newStart
end := expectedCheckpointState.newEnd
//
// Check checkpoint buffer
//
timeStamp := uint64(time.Now().Unix())
checkpointBufferTime := uint64(checkpointContext.CheckpointParams.CheckpointBufferTime.Seconds())
bufferedCheckpoint, err := util.GetBufferedCheckpoint(cp.cliCtx)
if err != nil {
cp.Logger.Debug("No buffered checkpoint", "bufferedCheckpoint", bufferedCheckpoint)
}
if bufferedCheckpoint != nil && !(bufferedCheckpoint.TimeStamp == 0 || ((timeStamp > bufferedCheckpoint.TimeStamp) && timeStamp-bufferedCheckpoint.TimeStamp >= checkpointBufferTime)) {
cp.Logger.Info("Checkpoint already exits in buffer", "Checkpoint", bufferedCheckpoint.String())
return nil
}
if err := cp.createAndSendCheckpointToHeimdall(checkpointContext, start, end); err != nil {
cp.Logger.Error("Error sending checkpoint to heimdall", "error", err)
return err
}
} else {
cp.Logger.Info("I am not the proposer. skipping newheader", "headerNumber", header.Number)
return
}
return nil
}
Heimdall 验证节点获取rootHash, 以交易的形式广播到Heimdall网络
// sendCheckpointToHeimdall - creates checkpoint msg and broadcasts to heimdall
func (cp *CheckpointProcessor) createAndSendCheckpointToHeimdall(checkpointContext *CheckpointContext, start uint64, end uint64) error {
cp.Logger.Debug("Initiating checkpoint to Heimdall", "start", start, "end", end)
// get checkpoint params
checkpointParams := checkpointContext.CheckpointParams
// Get root hash
root, err := cp.contractConnector.GetRootHash(start, end, checkpointParams.MaxCheckpointLength)
if err != nil {
return err
}
cp.Logger.Info("Root hash calculated", "rootHash", hmTypes.BytesToHeimdallHash(root))
var accountRootHash hmTypes.HeimdallHash
//Get DividendAccountRoot from HeimdallServer
if accountRootHash, err = cp.fetchDividendAccountRoot(); err != nil {
cp.Logger.Info("Error while fetching initial account root hash from HeimdallServer", "err", err)
return err
}
chainParams := checkpointContext.ChainmanagerParams.ChainParams
// create and send checkpoint message
msg := checkpointTypes.NewMsgCheckpointBlock(
hmTypes.BytesToHeimdallAddress(helper.GetAddress()),
start,
end,
hmTypes.BytesToHeimdallHash(root),
accountRootHash,
chainParams.BorChainID,
)
// return broadcast to heimdall
if err := cp.txBroadcaster.BroadcastToHeimdall(msg); err != nil {
cp.Logger.Error("Error while broadcasting checkpoint to heimdall", "error", err)
return err
}
return nil
}
sendCheckpointToRootchain
验证节点会轮循 Heimdall
区块 checkpoint
事件:
// ProcessBlockEvent - process Blockevents (BeginBlock, EndBlock events) from heimdall.
func (hl *HeimdallListener) ProcessBlockEvent(event sdk.StringEvent, blockHeight int64) {
hl.Logger.Info("Received block event from Heimdall", "eventType", event.Type)
eventBytes, err := json.Marshal(event)
if err != nil {
hl.Logger.Error("Error while parsing block event", "error", err, "eventType", event.Type)
return
}
switch event.Type {
case checkpointTypes.EventTypeCheckpoint: // 检查checkpoint事件
hl.sendBlockTask("sendCheckpointToRootchain", eventBytes, blockHeight)
case slashingTypes.EventTypeSlashLimit:
hl.sendBlockTask("sendTickToHeimdall", eventBytes, blockHeight)
case slashingTypes.EventTypeTickConfirm:
hl.sendBlockTask("sendTickToRootchain", eventBytes, blockHeight)
default:
hl.Logger.Debug("BlockEvent Type mismatch", "eventType", event.Type)
}
}
对于checkpoing
事件,执行sendCheckpointToRootchain
任务,判断自己是否为proposer
,并且是否应该提交checkpoint
(根据event 和rootchain上一个checkpoing 块号判断),然后向以太坊链上提交checkpoint
。
// sendCheckpointToRootchain - handles checkpoint confirmation event from heimdall.
// 1. check if i am the current proposer.
// 2. check if this checkpoint has to be submitted to rootchain
// 3. if so, create and broadcast checkpoint transaction to rootchain
func (cp *CheckpointProcessor) sendCheckpointToRootchain(eventBytes string, blockHeight int64) error {
cp.Logger.Info("Received sendCheckpointToRootchain request", "eventBytes", eventBytes, "blockHeight", blockHeight)
var event = sdk.StringEvent{}
if err := json.Unmarshal([]byte(eventBytes), &event); err != nil {
cp.Logger.Error("Error unmarshalling event from heimdall", "error", err)
return err
}
// var tx = sdk.TxResponse{}
// if err := json.Unmarshal([]byte(txBytes), &tx); err != nil {
// cp.Logger.Error("Error unmarshalling txResponse", "error", err)
// return err
// }
cp.Logger.Info("processing checkpoint confirmation event", "eventtype", event.Type)
isCurrentProposer, err := util.IsCurrentProposer(cp.cliCtx)
if err != nil {
cp.Logger.Error("Error checking isCurrentProposer in CheckpointConfirmation handler", "error", err)
return err
}
var startBlock uint64
var endBlock uint64
var txHash string
for _, attr := range event.Attributes {
if attr.Key == checkpointTypes.AttributeKeyStartBlock {
startBlock, _ = strconv.ParseUint(attr.Value, 10, 64)
}
if attr.Key == checkpointTypes.AttributeKeyEndBlock {
endBlock, _ = strconv.ParseUint(attr.Value, 10, 64)
}
if attr.Key == hmTypes.AttributeKeyTxHash {
txHash = attr.Value
}
}
checkpointContext, err := cp.getCheckpointContext()
if err != nil {
return err
}
shouldSend, err := cp.shouldSendCheckpoint(checkpointContext, startBlock, endBlock)
if err != nil {
return err
}
if shouldSend && isCurrentProposer {
txHash := common.FromHex(txHash)
if err := cp.createAndSendCheckpointToRootchain(checkpointContext, startBlock, endBlock, blockHeight, txHash); err != nil {
cp.Logger.Error("Error sending checkpoint to rootchain", "error", err)
return err
}
} else {
cp.Logger.Info("I am not the current proposer or checkpoint already sent. Ignoring", "eventType", event.Type)
return nil
}
return nil
}
createAndSendCheckpointToRootchain
会收集签名,然后调用链上合约。
// createAndSendCheckpointToRootchain prepares the data required for rootchain checkpoint submission
// and sends a transaction to rootchain
func (cp *CheckpointProcessor) createAndSendCheckpointToRootchain(checkpointContext *CheckpointContext, start uint64, end uint64, height int64, txHash []byte) error {
....
// get sigs
sigs, err := helper.FetchSideTxSigs(cp.httpClient, height, tx.Tx.Hash(), sideTxData) // 收集签名
if err != nil {
cp.Logger.Error("Error fetching votes for checkpoint tx", "height", height)
return err
}
shouldSend, err := cp.shouldSendCheckpoint(checkpointContext, start, end)
if err != nil {
return err
}
if shouldSend {
// chain manager params
chainParams := checkpointContext.ChainmanagerParams.ChainParams
// root chain address
rootChainAddress := chainParams.RootChainAddress.EthAddress()
// root chain instance
rootChainInstance, err := cp.contractConnector.GetRootChainInstance(rootChainAddress)
if err != nil {
cp.Logger.Info("Error while creating rootchain instance", "error", err)
return err
}
// 调用以太坊上合约
if err := cp.contractConnector.SendCheckpoint(sideTxData, sigs, rootChainAddress, rootChainInstance); err != nil {
cp.Logger.Info("Error submitting checkpoint to rootchain", "error", err)
return err
}
}
return nil
}
签名来源于投票vote
中, 其包含于block
中:
// Vote represents a prevote, precommit, or commit vote from validators for
// consensus.
type Vote struct {
Type SignedMsgType `json:"type"`
Height int64 `json:"height"`
Round int `json:"round"`
BlockID BlockID `json:"block_id"` // zero if vote is nil.
Timestamp time.Time `json:"timestamp"`
ValidatorAddress Address `json:"validator_address"`
ValidatorIndex int `json:"validator_index"`
Signature []byte `json:"signature"`
SideTxResults []SideTxResult `json:"side_tx_results"` // side-tx result [peppermint]
}
// SideTxResult side tx result for vote
type SideTxResult struct {
TxHash []byte `json:"tx_hash"`
Result int32 `json:"result"`
Sig []byte `json:"sig"` //收集的签名
}
以太坊上合约触发:
// Solidity: function submitCheckpoint(bytes data, uint256[3][] sigs) returns()
func (_Rootchain *RootchainTransactor) SubmitCheckpoint(opts *bind.TransactOpts, data []byte, sigs [][3]*big.Int) (*types.Transaction, error) {
return _Rootchain.contract.Transact(opts, "submitCheckpoint", data, sigs)
}
Ethereum
用户构建blockproof
燃烧证明
async submitCheckpoint(rootChain, receipt, proposer) {
const event = {
tx: await web3Child.eth.getTransaction(receipt.transactionHash),
receipt: await web3Child.eth.getTransactionReceipt(
receipt.transactionHash
),
block: await web3Child.eth.getBlock(
receipt.blockHash,
true /* returnTransactionObjects */
)
}
// rootChain expects the first checkpoint to start from block 0.
// However, ganache would already be running and would be much ahead of block 0.
// offset is used to treat the block of the first checkpoint to be 0
if (this.offset == null) {
this.offset = event.block.number
}
event.block.number -= this.offset // rootChain will thank you for this
const start = this.lastEndBlock + 1
const end = event.block.number
this.lastEndBlock = end
if (start > end) {
throw new Error(`Invalid end block number for checkpoint`, { start, end });
}
const headers = []
for (let i = start; i <= end; i++) {
const block = await web3Child.eth.getBlock(i + this.offset)
block.number = i
headers.push(getBlockHeader(block))
}
const blockHeader = getBlockHeader(event.block)
const tree = new MerkleTree(headers)
const root = ethUtils.bufferToHex(tree.getRoot())
const blockProof = await tree.getProof(blockHeader) // 获取blockproof
// tree
// .verify(blockHeader, end - start, tree.getRoot(), blockProof)
// .should.equal(true)
const { data, sigs } = utils.buildsubmitCheckpointPaylod(
proposer[0],
start,
end,
root,
proposer,
{ rewardsRootHash: ethUtils.keccak256('RandomState') }
)
const submitCheckpoint = await rootChain.submitCheckpoint(
data, sigs)
// const txProof = await getTxProof(event.tx, event.block)
// assert.isTrue(verifyTxProof(txProof), 'Tx proof must be valid (failed in js)')
// const receiptProof = await getReceiptProof(event.receipt, event.block, web3Child)
// assert.isTrue(verifyReceiptProof(receiptProof), 'Receipt proof must be valid (failed in js)')
const NewHeaderBlockEvent = submitCheckpoint.logs.find(
log => log.event === 'NewHeaderBlock'
)
return {
block: event.block,
blockProof,
headerNumber: NewHeaderBlockEvent.args.headerBlockId,
createdAt: (await rootChain.headerBlocks(
NewHeaderBlockEvent.args.headerBlockId
)).createdAt,
reference: await build(event)
}
}
调用以太坊上Exit
函数 :
function startExitWithBurntTokens(bytes calldata data) external {
RLPReader.RLPItem[] memory referenceTxData = data.toRlpItem().toList();
bytes memory receipt = referenceTxData[6].toBytes();
RLPReader.RLPItem[] memory inputItems = receipt.toRlpItem().toList();
uint256 logIndex = referenceTxData[9].toUint();
require(logIndex < MAX_LOGS, "Supporting a max of 10 logs");
uint256 age = withdrawManager.verifyInclusion( // Merkle 路径验证
data,
0, /* offset */
false /* verifyTxInclusion */
);
inputItems = inputItems[3].toList()[logIndex].toList(); // select log based on given logIndex
// "address" (contract address that emitted the log) field in the receipt
address childToken = RLPReader.toAddress(inputItems[0]);
bytes memory logData = inputItems[2].toBytes();
inputItems = inputItems[1].toList(); // topics
// now, inputItems[i] refers to i-th (0-based) topic in the topics array
// event Withdraw(address indexed token, address indexed from, uint256 amountOrTokenId, uint256 input1, uint256 output1)
require(
bytes32(inputItems[0].toUint()) == WITHDRAW_EVENT_SIG,
"Not a withdraw event signature"
);
address rootToken = address(RLPReader.toUint(inputItems[1]));
require(
msg.sender == address(inputItems[2].toUint()), // from
"Withdrawer and burn exit tx do not match"
);
uint256 exitAmount = BytesLib.toUint(logData, 0); // amountOrTokenId
withdrawManager.addExitToQueue(
msg.sender,
childToken,
rootToken,
exitAmount,
bytes32(0x0),
true, /* isRegularExit */
age << 1
);
}
最终退出调用合约processExiits
函数:
function processExits(address _token) public {
uint256 exitableAt;
uint256 exitId;
PriorityQueue exitQueue = PriorityQueue(exitsQueues[_token]);
while (exitQueue.currentSize() > 0 && gasleft() > ON_FINALIZE_GAS_LIMIT) {
(exitableAt, exitId) = exitQueue.getMin();
exitId = (exitableAt << 128) | exitId;
PlasmaExit memory currentExit = exits[exitId];
// Stop processing exits if the exit that is next is queue is still in its challenge period
if (exitableAt > block.timestamp) return;
exitQueue.delMin();
// If the exitNft was deleted as a result of a challenge, skip processing this exit
if (!exitNft.exists(exitId)) continue;
address exitor = exitNft.ownerOf(exitId);
exits[exitId].owner = exitor;
exitNft.burn(exitId);
// If finalizing a particular exit is reverting, it will block any following exits from being processed.
// Hence, call predicate.onFinalizeExit in a revertless manner.
// (bool success, bytes memory result) =
currentExit.predicate.call(
abi.encodeWithSignature("onFinalizeExit(bytes)", encodeExitForProcessExit(exitId)) //最终退出
);
emit Withdraw(exitId, exitor, _token, currentExit.receiptAmountOrNFTId);
if (!currentExit.isRegularExit) {
// return the bond amount if this was a MoreVp style exit
address(uint160(exitor)).send(BOND_AMOUNT);
}
}
}
网友评论