Skip to content

Commit

Permalink
Feat/challenger (#14)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
sh-cha authored Sep 11, 2024
1 parent 108d0f8 commit 5e6554f
Show file tree
Hide file tree
Showing 104 changed files with 3,995 additions and 1,132 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ build_tags = netgo
build_tags += $(BUILD_TAGS)
build_tags := $(strip $(build_tags))

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

ldflags += $(LDFLAGS)
ldflags := $(strip $(ldflags))
Expand Down
29 changes: 11 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This repository contains the Go implementation of OPinit bots.
## Components

- [Executor](./executor)
- [Challenger](./challenger)

## How to Use

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

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

| L1 Node | MiniMove | MiniWasm | MiniEVM |
| ------- | -------- | -------- | ------- |
| v0.4.2 | v0.4.0 | v0.4.0 | v0.4.0 |
| L1 Node | MiniMove | MiniWasm | MiniEVM |
| ------- | -------- | -------- | ------- |
| v0.4.7 | v0.4.1 | v0.4.1 | - |

### Build and Configure

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

- `executor`
- `challenger`

### Register keys

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

log level can be set by using `--log-level` flag. Default log level is `info`.

Options
- `--log-level`: log level can be set. Default log level is `info`.
- `--polling-interval`: polling interval can be set. Default polling interval is `100ms`.
- `--config`: config file name can be set. Default config file name is `[bot-name].json`.
- `--home`: home dir can be set. Default home dir is `~/.opinit`.

### Reset Bot DB

To reset the bot database, use the following command:

```bash
opinitd reset-db [bot-name]
```

### Query status

```bash
curl localhost:3000/status
```

### Query withdrawals

```bash
curl localhost:3000/withdrawal/{sequence} | jq . > ./withdrawal-info.json
initiad tx ophost finalize-token-withdrawal ./withdrawal-info.json --gas= --gas-prices= --chain-id= --from=
```
39 changes: 25 additions & 14 deletions bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import (
"fmt"
"io"
"os"
"path"

"go.uber.org/zap"

bottypes "github.com/initia-labs/opinit-bots-go/bot/types"
"github.com/initia-labs/opinit-bots-go/db"
"github.com/initia-labs/opinit-bots-go/executor"
executortypes "github.com/initia-labs/opinit-bots-go/executor/types"
"github.com/initia-labs/opinit-bots-go/server"
bottypes "github.com/initia-labs/opinit-bots/bot/types"
"github.com/initia-labs/opinit-bots/challenger"
challengertypes "github.com/initia-labs/opinit-bots/challenger/types"
"github.com/initia-labs/opinit-bots/db"
"github.com/initia-labs/opinit-bots/executor"
executortypes "github.com/initia-labs/opinit-bots/executor/types"
"github.com/initia-labs/opinit-bots/server"
)

func LoadJsonConfig(path string, config bottypes.Config) error {
Expand All @@ -35,24 +36,34 @@ func LoadJsonConfig(path string, config bottypes.Config) error {
return nil
}

func NewBot(name bottypes.BotType, logger *zap.Logger, homePath string, configName string) (bottypes.Bot, error) {
switch name {
func NewBot(botType bottypes.BotType, logger *zap.Logger, homePath string, configPath string) (bottypes.Bot, error) {
err := botType.Validate()
if err != nil {
return nil, err
}

db, err := db.NewDB(getDBPath(homePath, botType))
if err != nil {
return nil, err
}
server := server.NewServer()

switch botType {
case bottypes.BotTypeExecutor:
cfg := &executortypes.Config{}

configPath := path.Join(homePath, configName)
err := LoadJsonConfig(configPath, cfg)
if err != nil {
return nil, err
}
db, err := db.NewDB(getDBPath(homePath, name))
return executor.NewExecutor(cfg, db, server, logger.Named("executor"), homePath), nil
case bottypes.BotTypeChallenger:
cfg := &challengertypes.Config{}
err := LoadJsonConfig(configPath, cfg)
if err != nil {
return nil, err
}
server := server.NewServer()
return executor.NewExecutor(cfg, db, server, logger.Named("executor"), homePath), nil
return challenger.NewChallenger(cfg, db, server, logger.Named("challenger"), homePath), nil
}

return nil, errors.New("not providing bot name")
}

Expand Down
17 changes: 15 additions & 2 deletions bot/types/const.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
package types

import (
"fmt"
)

type BotType string

const (
BotTypeExecutor BotType = "executor"
BotTypeExecutor BotType = "executor"
BotTypeChallenger BotType = "challenger"
)

func (b BotType) Validate() error {
if b != BotTypeExecutor && b != BotTypeChallenger {
return fmt.Errorf("invalid bot type: %s", b)
}
return nil
}

func BotTypeFromString(name string) BotType {
switch name {
case "executor":
return BotTypeExecutor
case "challenger":
return BotTypeChallenger
}

panic("unknown bot type")
}
207 changes: 207 additions & 0 deletions challenger/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# Challenger

The Challenger is responsible for
1. verifying that the `MsgInitiateTokenDeposit` event is properly relayed to `MsgFinalizeTokenDeposit`.
2. checking whether `MsgInitiateTokenDeposit` was relayed on time.
3. verifying that the `Oracle` data is properly relayed to `MsgUpdateOracle`.
4. checking whether `Oracle` was relayed on time.
5. verifying that the `OutputRoot` submitted with `MsgProposeOutput` is correct.
6. checking whether next `MsgProposeOutput` was submitted on time.

## Config

To configure the Executor, fill in the values in the `~/.opinit/executor.json` file.

```json
{
// Version is the version used to build output root.
"version": 1,
// ListenAddress is the address to listen for incoming requests.
"listen_address": "localhost:3001",
"l1_node": {
"chain_id": "testnet-l1-1",
"bech32_prefix": "init",
"rpc_address": "tcp://localhost:26657",
},
"l2_node": {
"chain_id": "testnet-l2-1",
"bech32_prefix": "init",
"rpc_address": "tcp://localhost:27657",
},
// L2StartHeight is the height to start the l2 node. If it is 0, it will start from the latest height.
// If the latest height stored in the db is not 0, this config is ignored.
// L2 starts from the last submitted output l2 block number + 1 before L2StartHeight.
// L1 starts from the block number of the output tx + 1
"l2_start_height": 0,
}
```

### Start height config examples
If the latest height stored in the db is not 0, start height config is ignored.

```
Output tx 1
- L1BlockNumber: 10
- L2BlockNumber: 100
Output tx 2
- L1BlockNumber: 20
- L2BlockNumber: 200
InitializeTokenDeposit tx 1
- Height: 5
- L1Sequence: 1
InitializeTokenDeposit tx 2
- Height: 15
- L1Sequence: 2
FinalizedTokenDeposit tx 1
- L1Sequence: 1
FinalizedTokenDeposit tx 2
- L1Sequence: 2
```

#### Config 1
```json
{
l2_start_height: 150,
}
```
When Child's last l1 Sequence is `2`,
- L1 starts from the height 10 + 1 = 11
- L2 starts from the height 100 + 1 = 101


## Handler rules for the components of the Challenger
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.
#### The challenger can check the generated `Challenges` and decide what action to take.

## Deposit
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.
```go
// Deposit is the challenge event for the deposit
type Deposit struct {
EventType string `json:"event_type"`
Sequence uint64 `json:"sequence"`
L1BlockHeight uint64 `json:"l1_block_height"`
From string `json:"from"`
To string `json:"to"`
L1Denom string `json:"l1_denom"`
Amount string `json:"amount"`
Time time.Time `json:"time"`
Timeout bool `json:"timeout"`
}
```

## Output
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.
```go
// Output is the challenge event for the output
type Output struct {
EventType string `json:"event_type"`
L2BlockNumber uint64 `json:"l2_block_number"`
OutputIndex uint64 `json:"output_index"`
OutputRoot []byte `json:"output_root"`
Time time.Time `json:"time"`
Timeout bool `json:"timeout"`
}
```

## Oracle
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.

## Batch
Batch data is not verified by the challenger bot.
#### TODO
* Challenger runs a L2 node it in rollup sync challenge mode in CometBFT to check whether the submitted batch is replayed properly.

## Query

### Status
```bash
curl localhost:3001/status
```

```json
{
"bridge_id": 0,
"host": {
"node": {
"last_block_height": 0
},
"last_output_index": 0,
"last_output_time": "",
"num_pending_events": {}
},
"child": {
"node": {
"last_block_height": 0
},
"last_updated_oracle_height": 0,
"last_finalized_deposit_l1_block_height": 0,
"last_finalized_deposit_l1_sequence": 0,
"last_withdrawal_l2_sequence": 0,
"working_tree_index": 0,
"finalizing_block_height": 0,
"last_output_submission_time": "",
"next_output_submission_time": "",
"num_pending_events": {}
},
"latest_challenges": []
}
```

### Challenges
```bash
curl localhost:3001/challenges/{page}
```

```json
[
{
"event_type": "Oracle",
"id": {
"type": 2,
"id": 136
},
"log": "event timeout: Oracle{L1Height: 136, Data: nUYSP4e3jTUBk33jFSYuWW28U+uTO+g3IO5/iZfbDTo, Time: 2024-09-11 06:01:21.257012 +0000 UTC}",
"timestamp": "2024-09-11T06:05:21.284479Z"
},
]
```

### Pending events
```bash
curl localhost:3001/pending_events/host
```

```json
[
{
"event_type": "Output",
"l2_block_number": 2394,
"output_index": 7,
"output_root": "Lx+k0GuHi6jPcO5yA6dUwI/l0h4b25awy973F6CirYs=",
"time": "2024-09-11T06:28:54.186941Z",
"timeout": false
}
]
```

```bash
curl localhost:3001/pending_events/child
```

```json
[
{
"event_type": "Oracle",
"l1_height": 1025,
"data": "49Y7iryZGsQYzpao+rxR2Vfaz6owp3s5vlPnkboXwr0=",
"time": "2024-09-11T06:31:28.657245Z",
"timeout": false
}
]
```
Loading

0 comments on commit 5e6554f

Please sign in to comment.