Skip to content

Commit 5e6554f

Browse files
authored
Feat/challenger (#14)
* move basehost and basechild out * initializer to basehost and basechild * create message event parser into basehost and basechild * implement basic challenge function comparing two tx events in deposit and output * format and lint * bug fix when loading working tree * wait one minute if no output exists * add challenger to cmd * bug fix initializing height & empty event queue * use opinit version 1 not 0 * add oracle challenge * renew challenger * load pending events & add pending events & latest challenges to status * add timeout challenge & challenge endpoint & pending event status * add timeout checker * show 10 latest challenges as sorted * lint & format * Feat/batch (#15) * change batch spec adding header bytes to batch data * bump opinit version * delete oracle option * bug fix marshal unmarshal batch data * make broadcaster initialize function to combine prepare func with context * add logs & num pending events status * update readme & add challenger status
1 parent 108d0f8 commit 5e6554f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+3995
-1132
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ build_tags = netgo
2222
build_tags += $(BUILD_TAGS)
2323
build_tags := $(strip $(build_tags))
2424

25-
ldflags = -X github.com/initia-labs/opinit-bots-go/version.Version=$(VERSION) \
26-
-X github.com/initia-labs/opinit-bots-go/version.GitCommit=$(COMMIT)
25+
ldflags = -X github.com/initia-labs/opinit-bots/version.Version=$(VERSION) \
26+
-X github.com/initia-labs/opinit-bots/version.GitCommit=$(COMMIT)
2727

2828
ldflags += $(LDFLAGS)
2929
ldflags := $(strip $(ldflags))

README.md

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This repository contains the Go implementation of OPinit bots.
55
## Components
66

77
- [Executor](./executor)
8+
- [Challenger](./challenger)
89

910
## How to Use
1011

@@ -16,9 +17,9 @@ Before running OPinit bots, make sure you have the following prerequisites insta
1617

1718
To ensure compatibility with the node version, check the following versions:
1819

19-
| L1 Node | MiniMove | MiniWasm | MiniEVM |
20-
| ------- | -------- | -------- | ------- |
21-
| v0.4.2 | v0.4.0 | v0.4.0 | v0.4.0 |
20+
| L1 Node | MiniMove | MiniWasm | MiniEVM |
21+
| ------- | -------- | -------- | ------- |
22+
| v0.4.7 | v0.4.1 | v0.4.1 | - |
2223

2324
### Build and Configure
2425

@@ -37,6 +38,7 @@ Default config path is `~/.opinit/[bot-name].json`
3738
Supported bot names
3839

3940
- `executor`
41+
- `challenger`
4042

4143
### Register keys
4244

@@ -58,25 +60,16 @@ To start the bot, use the following command:
5860
opinitd start [bot-name]
5961
```
6062

61-
log level can be set by using `--log-level` flag. Default log level is `info`.
62-
63+
Options
64+
- `--log-level`: log level can be set. Default log level is `info`.
65+
- `--polling-interval`: polling interval can be set. Default polling interval is `100ms`.
66+
- `--config`: config file name can be set. Default config file name is `[bot-name].json`.
67+
- `--home`: home dir can be set. Default home dir is `~/.opinit`.
68+
6369
### Reset Bot DB
6470

6571
To reset the bot database, use the following command:
6672

6773
```bash
6874
opinitd reset-db [bot-name]
6975
```
70-
71-
### Query status
72-
73-
```bash
74-
curl localhost:3000/status
75-
```
76-
77-
### Query withdrawals
78-
79-
```bash
80-
curl localhost:3000/withdrawal/{sequence} | jq . > ./withdrawal-info.json
81-
initiad tx ophost finalize-token-withdrawal ./withdrawal-info.json --gas= --gas-prices= --chain-id= --from=
82-
```

bot/bot.go

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ import (
66
"fmt"
77
"io"
88
"os"
9-
"path"
109

1110
"go.uber.org/zap"
1211

13-
bottypes "github.com/initia-labs/opinit-bots-go/bot/types"
14-
"github.com/initia-labs/opinit-bots-go/db"
15-
"github.com/initia-labs/opinit-bots-go/executor"
16-
executortypes "github.com/initia-labs/opinit-bots-go/executor/types"
17-
"github.com/initia-labs/opinit-bots-go/server"
12+
bottypes "github.com/initia-labs/opinit-bots/bot/types"
13+
"github.com/initia-labs/opinit-bots/challenger"
14+
challengertypes "github.com/initia-labs/opinit-bots/challenger/types"
15+
"github.com/initia-labs/opinit-bots/db"
16+
"github.com/initia-labs/opinit-bots/executor"
17+
executortypes "github.com/initia-labs/opinit-bots/executor/types"
18+
"github.com/initia-labs/opinit-bots/server"
1819
)
1920

2021
func LoadJsonConfig(path string, config bottypes.Config) error {
@@ -35,24 +36,34 @@ func LoadJsonConfig(path string, config bottypes.Config) error {
3536
return nil
3637
}
3738

38-
func NewBot(name bottypes.BotType, logger *zap.Logger, homePath string, configName string) (bottypes.Bot, error) {
39-
switch name {
39+
func NewBot(botType bottypes.BotType, logger *zap.Logger, homePath string, configPath string) (bottypes.Bot, error) {
40+
err := botType.Validate()
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
db, err := db.NewDB(getDBPath(homePath, botType))
46+
if err != nil {
47+
return nil, err
48+
}
49+
server := server.NewServer()
50+
51+
switch botType {
4052
case bottypes.BotTypeExecutor:
4153
cfg := &executortypes.Config{}
42-
43-
configPath := path.Join(homePath, configName)
4454
err := LoadJsonConfig(configPath, cfg)
4555
if err != nil {
4656
return nil, err
4757
}
48-
db, err := db.NewDB(getDBPath(homePath, name))
58+
return executor.NewExecutor(cfg, db, server, logger.Named("executor"), homePath), nil
59+
case bottypes.BotTypeChallenger:
60+
cfg := &challengertypes.Config{}
61+
err := LoadJsonConfig(configPath, cfg)
4962
if err != nil {
5063
return nil, err
5164
}
52-
server := server.NewServer()
53-
return executor.NewExecutor(cfg, db, server, logger.Named("executor"), homePath), nil
65+
return challenger.NewChallenger(cfg, db, server, logger.Named("challenger"), homePath), nil
5466
}
55-
5667
return nil, errors.New("not providing bot name")
5768
}
5869

bot/types/const.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
11
package types
22

3+
import (
4+
"fmt"
5+
)
6+
37
type BotType string
48

59
const (
6-
BotTypeExecutor BotType = "executor"
10+
BotTypeExecutor BotType = "executor"
11+
BotTypeChallenger BotType = "challenger"
712
)
813

14+
func (b BotType) Validate() error {
15+
if b != BotTypeExecutor && b != BotTypeChallenger {
16+
return fmt.Errorf("invalid bot type: %s", b)
17+
}
18+
return nil
19+
}
20+
921
func BotTypeFromString(name string) BotType {
1022
switch name {
1123
case "executor":
1224
return BotTypeExecutor
25+
case "challenger":
26+
return BotTypeChallenger
1327
}
14-
1528
panic("unknown bot type")
1629
}

challenger/README.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
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

Comments
 (0)