|
| 1 | +# Challenger |
| 2 | + |
| 3 | +The Challenger is responsible for |
| 4 | +1. verifying that the `MsgInitiateTokenDeposit` event is properly relayed to `MsgFinalizeTokenDeposit`. |
| 5 | +2. checking whether `MsgInitiateTokenDeposit` was relayed on time. |
| 6 | +3. verifying that the `Oracle` data is properly relayed to `MsgUpdateOracle`. |
| 7 | +4. checking whether `Oracle` was relayed on time. |
| 8 | +5. verifying that the `OutputRoot` submitted with `MsgProposeOutput` is correct. |
| 9 | +6. checking whether next `MsgProposeOutput` was submitted on time. |
| 10 | + |
| 11 | +## Config |
| 12 | + |
| 13 | +To configure the Executor, fill in the values in the `~/.opinit/executor.json` file. |
| 14 | + |
| 15 | +```json |
| 16 | +{ |
| 17 | + // Version is the version used to build output root. |
| 18 | + "version": 1, |
| 19 | + // ListenAddress is the address to listen for incoming requests. |
| 20 | + "listen_address": "localhost:3001", |
| 21 | + "l1_node": { |
| 22 | + "chain_id": "testnet-l1-1", |
| 23 | + "bech32_prefix": "init", |
| 24 | + "rpc_address": "tcp://localhost:26657", |
| 25 | + }, |
| 26 | + "l2_node": { |
| 27 | + "chain_id": "testnet-l2-1", |
| 28 | + "bech32_prefix": "init", |
| 29 | + "rpc_address": "tcp://localhost:27657", |
| 30 | + }, |
| 31 | + // L2StartHeight is the height to start the l2 node. If it is 0, it will start from the latest height. |
| 32 | + // If the latest height stored in the db is not 0, this config is ignored. |
| 33 | + // L2 starts from the last submitted output l2 block number + 1 before L2StartHeight. |
| 34 | + // L1 starts from the block number of the output tx + 1 |
| 35 | + "l2_start_height": 0, |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +### Start height config examples |
| 40 | +If the latest height stored in the db is not 0, start height config is ignored. |
| 41 | + |
| 42 | +``` |
| 43 | +Output tx 1 |
| 44 | +- L1BlockNumber: 10 |
| 45 | +- L2BlockNumber: 100 |
| 46 | +
|
| 47 | +Output tx 2 |
| 48 | +- L1BlockNumber: 20 |
| 49 | +- L2BlockNumber: 200 |
| 50 | +
|
| 51 | +InitializeTokenDeposit tx 1 |
| 52 | +- Height: 5 |
| 53 | +- L1Sequence: 1 |
| 54 | +
|
| 55 | +InitializeTokenDeposit tx 2 |
| 56 | +- Height: 15 |
| 57 | +- L1Sequence: 2 |
| 58 | +
|
| 59 | +FinalizedTokenDeposit tx 1 |
| 60 | +- L1Sequence: 1 |
| 61 | +
|
| 62 | +FinalizedTokenDeposit tx 2 |
| 63 | +- L1Sequence: 2 |
| 64 | +``` |
| 65 | + |
| 66 | +#### Config 1 |
| 67 | +```json |
| 68 | +{ |
| 69 | + l2_start_height: 150, |
| 70 | +} |
| 71 | +``` |
| 72 | +When Child's last l1 Sequence is `2`, |
| 73 | +- L1 starts from the height 10 + 1 = 11 |
| 74 | +- L2 starts from the height 100 + 1 = 101 |
| 75 | + |
| 76 | + |
| 77 | +## Handler rules for the components of the Challenger |
| 78 | +For registered events or tx handlers, work processed in a block is atomically saved as `pending events`. Therefore, if `pending events` with the `ChallengeEvent` interface cannot be processed due to an interrupt or error, it is guaranteed to be read from the DB and processed. When an event matching the pending event comes in and is processed, or when the block time exceeds the event's timeout, a `Challenge` is created and stored in the DB. |
| 79 | +#### The challenger can check the generated `Challenges` and decide what action to take. |
| 80 | + |
| 81 | +## Deposit |
| 82 | +When the `initiate_token_deposit` event is detected in l1, saves it as a `Deposit` challenge event and check if it is the same as the `MsgFinalizeTokenDeposit` for the same sequence. |
| 83 | +```go |
| 84 | +// Deposit is the challenge event for the deposit |
| 85 | +type Deposit struct { |
| 86 | + EventType string `json:"event_type"` |
| 87 | + Sequence uint64 `json:"sequence"` |
| 88 | + L1BlockHeight uint64 `json:"l1_block_height"` |
| 89 | + From string `json:"from"` |
| 90 | + To string `json:"to"` |
| 91 | + L1Denom string `json:"l1_denom"` |
| 92 | + Amount string `json:"amount"` |
| 93 | + Time time.Time `json:"time"` |
| 94 | + Timeout bool `json:"timeout"` |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +## Output |
| 99 | +When the `propose_output` event is detected in l1, saves it as a `Output` challenge event, replays up to l2 block number and check if `OutputRoot` is the same as submitted. |
| 100 | +```go |
| 101 | +// Output is the challenge event for the output |
| 102 | +type Output struct { |
| 103 | + EventType string `json:"event_type"` |
| 104 | + L2BlockNumber uint64 `json:"l2_block_number"` |
| 105 | + OutputIndex uint64 `json:"output_index"` |
| 106 | + OutputRoot []byte `json:"output_root"` |
| 107 | + Time time.Time `json:"time"` |
| 108 | + Timeout bool `json:"timeout"` |
| 109 | +} |
| 110 | +``` |
| 111 | + |
| 112 | +## Oracle |
| 113 | +If `oracle_enable` is turned on in bridge config, saves bytes of the 0th Tx as a `Oracle` challenge event and check if it is the same data in the `MsgUpdateOracle` for the l1 height. |
| 114 | + |
| 115 | +## Batch |
| 116 | +Batch data is not verified by the challenger bot. |
| 117 | +#### TODO |
| 118 | +* Challenger runs a L2 node it in rollup sync challenge mode in CometBFT to check whether the submitted batch is replayed properly. |
| 119 | + |
| 120 | +## Query |
| 121 | + |
| 122 | +### Status |
| 123 | +```bash |
| 124 | +curl localhost:3001/status |
| 125 | +``` |
| 126 | + |
| 127 | +```json |
| 128 | +{ |
| 129 | + "bridge_id": 0, |
| 130 | + "host": { |
| 131 | + "node": { |
| 132 | + "last_block_height": 0 |
| 133 | + }, |
| 134 | + "last_output_index": 0, |
| 135 | + "last_output_time": "", |
| 136 | + "num_pending_events": {} |
| 137 | + }, |
| 138 | + "child": { |
| 139 | + "node": { |
| 140 | + "last_block_height": 0 |
| 141 | + }, |
| 142 | + "last_updated_oracle_height": 0, |
| 143 | + "last_finalized_deposit_l1_block_height": 0, |
| 144 | + "last_finalized_deposit_l1_sequence": 0, |
| 145 | + "last_withdrawal_l2_sequence": 0, |
| 146 | + "working_tree_index": 0, |
| 147 | + "finalizing_block_height": 0, |
| 148 | + "last_output_submission_time": "", |
| 149 | + "next_output_submission_time": "", |
| 150 | + "num_pending_events": {} |
| 151 | + }, |
| 152 | + "latest_challenges": [] |
| 153 | +} |
| 154 | +``` |
| 155 | + |
| 156 | +### Challenges |
| 157 | +```bash |
| 158 | +curl localhost:3001/challenges/{page} |
| 159 | +``` |
| 160 | + |
| 161 | +```json |
| 162 | +[ |
| 163 | + { |
| 164 | + "event_type": "Oracle", |
| 165 | + "id": { |
| 166 | + "type": 2, |
| 167 | + "id": 136 |
| 168 | + }, |
| 169 | + "log": "event timeout: Oracle{L1Height: 136, Data: nUYSP4e3jTUBk33jFSYuWW28U+uTO+g3IO5/iZfbDTo, Time: 2024-09-11 06:01:21.257012 +0000 UTC}", |
| 170 | + "timestamp": "2024-09-11T06:05:21.284479Z" |
| 171 | + }, |
| 172 | +] |
| 173 | +``` |
| 174 | + |
| 175 | +### Pending events |
| 176 | +```bash |
| 177 | +curl localhost:3001/pending_events/host |
| 178 | +``` |
| 179 | + |
| 180 | +```json |
| 181 | +[ |
| 182 | + { |
| 183 | + "event_type": "Output", |
| 184 | + "l2_block_number": 2394, |
| 185 | + "output_index": 7, |
| 186 | + "output_root": "Lx+k0GuHi6jPcO5yA6dUwI/l0h4b25awy973F6CirYs=", |
| 187 | + "time": "2024-09-11T06:28:54.186941Z", |
| 188 | + "timeout": false |
| 189 | + } |
| 190 | +] |
| 191 | +``` |
| 192 | + |
| 193 | +```bash |
| 194 | +curl localhost:3001/pending_events/child |
| 195 | +``` |
| 196 | + |
| 197 | +```json |
| 198 | +[ |
| 199 | + { |
| 200 | + "event_type": "Oracle", |
| 201 | + "l1_height": 1025, |
| 202 | + "data": "49Y7iryZGsQYzpao+rxR2Vfaz6owp3s5vlPnkboXwr0=", |
| 203 | + "time": "2024-09-11T06:31:28.657245Z", |
| 204 | + "timeout": false |
| 205 | + } |
| 206 | +] |
| 207 | +``` |
0 commit comments