Skip to content

Commit

Permalink
feat: create sync reward transaction when in-contract call fails (#12)
Browse files Browse the repository at this point in the history
This commit makes the bridge service automatically send the sync reward
transaction if enabled in configuration when the in-contract call fails.

We can enable this feature via LISTENERS__RONIN__ENABLESYNCREWARD when using the
docker image.
  • Loading branch information
minh-bq authored Jul 5, 2024
1 parent 7b22886 commit e66460b
Show file tree
Hide file tree
Showing 10 changed files with 1,947 additions and 5 deletions.
2 changes: 2 additions & 0 deletions cmd/bridge/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"syscall"

bridgeReward "github.com/axieinfinity/bridge-v2/generated_contracts/bridge_reward"
"github.com/axieinfinity/bridge-v2/internal"
"github.com/axieinfinity/bridge-v2/stats"
"github.com/spf13/viper"
Expand Down Expand Up @@ -125,6 +126,7 @@ func bridge(ctx *cli.Context) {
if err != nil {
panic(err)
}
bridgeCore.ABIMaps["BridgeReward"] = bridgeReward.BridgeRewardMetaData

// setup stats
setupStats(cfg, RoninNetwork, db)
Expand Down
15 changes: 14 additions & 1 deletion config/config.mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
"EthGateway": "0x64192819Ac13Ef72bF6b5AE239AC672B43a9AF08",
"EthGovernance": "0xB255D6A720BB7c39fee173cE22113397119cB930",
"Governance": "0x946397deDFd2f79b75a72B322944a21C3240c9c3",
"TrustedOrganization": "0x98D0230884448B3E2f09a177433D60fb1E19C090"
"TrustedOrganization": "0x98D0230884448B3E2f09a177433D60fb1E19C090",
"BridgeReward": "0x796a163a21e9a659fc9773166e0afdc1eb01aad1"
},
"stats": {
"node": "",
"host": "",
"secret": ""
},
"enableSyncReward": false,
"subscriptions": {
"MainchainWithdrewSubscription": {
"to": "0x0CF8fF40a508bdBc39fBe1Bb679dCBa64E65C7Df",
Expand Down Expand Up @@ -91,6 +93,17 @@
"callbacks": {
"Ronin": "BridgeOperatorsApprovedCallback"
}
},
"ExternalCallFailedSubscription": {
"to": "0x3Fb325b251eE80945d3fc8c7692f5aFFCA1B8bC2",
"type": 1,
"handler": {
"contract": "BridgeTracking",
"name": "ExternalCallFailed"
},
"callbacks": {
"Ronin": "ExternalCallFailedCallback"
}
}
}
},
Expand Down
15 changes: 14 additions & 1 deletion config/config.testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
"processWithinBlocks": 864000,
"contracts": {
"Gateway": "0xCee681C9108c42C710c6A8A949307D5F13C9F3ca",
"EthGateway": "0x06855f31df1d3d25ce486cf09db49bda535d2a9e"
"EthGateway": "0x06855f31df1d3d25ce486cf09db49bda535d2a9e",
"BridgeReward": "0x6E19cF519b7B83F7CE719B6d30232485d9609D95"
},
"stats": {
"node": "",
"host": "",
"secret": ""
},
"enableSyncReward": false,
"subscriptions": {
"MainchainWithdrewSubscription": {
"to": "0xCee681C9108c42C710c6A8A949307D5F13C9F3ca",
Expand Down Expand Up @@ -66,6 +68,17 @@
"callbacks": {
"Ronin": "ProvideReceiptSignatureAgainCallback"
}
},
"ExternalCallFailedSubscription": {
"to": "0x880c5da7bFF9740464287EBFE381Be1cCCCE4FEA",
"type": 1,
"handler": {
"contract": "BridgeTracking",
"name": "ExternalCallFailed"
},
"callbacks": {
"Ronin": "ExternalCallFailedCallback"
}
}
}
},
Expand Down
1,813 changes: 1,813 additions & 0 deletions generated_contracts/bridge_reward/bridge_reward.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module github.com/axieinfinity/bridge-v2

replace (
github.com/axieinfinity/bridge-core => github.com/ronin-chain/bridge-core v0.1.3-0.20240401060412-3ff265961e35
github.com/axieinfinity/bridge-core => github.com/ronin-chain/bridge-core v0.1.3-0.20240625082548-579437b59387
github.com/ethereum/go-ethereum => github.com/axieinfinity/ronin v1.10.4-0.20240117085004-bf2f0d1787d0
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,8 @@ github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/ronin-chain/bridge-core v0.1.3-0.20240401060412-3ff265961e35 h1:kZFtDSjFRt9DcP1+bCElKJ7HHNqzRTU+6VEOkQB1ipc=
github.com/ronin-chain/bridge-core v0.1.3-0.20240401060412-3ff265961e35/go.mod h1:X6QX0BsMR432Ez8ShNavbC0Sp7rtVthfGWdLtg174ag=
github.com/ronin-chain/bridge-core v0.1.3-0.20240625082548-579437b59387 h1:5oKcWsJOpY6NbHxMr0RwWb8OVKvPwpvKsok2vqmYClI=
github.com/ronin-chain/bridge-core v0.1.3-0.20240625082548-579437b59387/go.mod h1:X6QX0BsMR432Ez8ShNavbC0Sp7rtVthfGWdLtg174ag=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
Expand Down
46 changes: 46 additions & 0 deletions listener/ronin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
ethGovernance "github.com/axieinfinity/bridge-core/generated_contracts/ethereum/governance"
"github.com/ethereum/go-ethereum/crypto"

bridgeTracking "github.com/axieinfinity/bridge-core/generated_contracts/ronin/bridge_tracking"
roninTrustedOrganization "github.com/axieinfinity/bridge-core/generated_contracts/ronin/trusted_organization"

bridgeCore "github.com/axieinfinity/bridge-core"
Expand Down Expand Up @@ -294,6 +295,51 @@ func (l *RoninListener) WithdrewCallback(fromChainId *big.Int, tx bridgeCore.Tra
return l.bridgeStore.GetTaskStore().Save(ackWithdrewTask)
}

func (l *RoninListener) ExternalCallFailedCallback(fromChainId *big.Int, tx bridgeCore.Transaction, data []byte) error {
if !l.config.EnableSyncReward {
return nil
}

failedEvent := new(bridgeTracking.BridgeTrackingExternalCallFailed)
bridgeTrackingAbi, err := bridgeTracking.BridgeTrackingMetaData.GetAbi()
if err != nil {
return err
}

if err = l.utilsWrapper.UnpackLog(*bridgeTrackingAbi, failedEvent, "ExternalCallFailed", data); err != nil {
return err
}

// We only process ExternalCallFailed event with to = bridge reward and
// msgSig = IBridgeReward.execSyncReward.selector
// execSyncReward(address[],uint256[],uint256,uint256,uint256)
var execSyncRewardSelector = [4]byte(common.Hex2Bytes("6bcb6fd6"))
if failedEvent.To != common.HexToAddress(l.config.Contracts[task.BRIDGE_REWARD_CONTRACT]) {
return nil
}
if failedEvent.MsgSig != execSyncRewardSelector {
return nil
}

chainId, err := l.GetChainID()
if err != nil {
return err
}
syncRewardTask := &models.Task{
ChainId: hexutil.EncodeBig(chainId),
FromChainId: hexutil.EncodeBig(fromChainId),
FromTransaction: tx.GetHash().Hex(),
Type: task.BRIDGE_SYNC_REWARD_TASK,
Data: common.Bytes2Hex(data),
Retries: 0,
Status: task.STATUS_PENDING,
LastError: "",
CreatedAt: time.Now().Unix(),
}

return l.bridgeStore.GetTaskStore().Save(syncRewardTask)
}

type RoninCallBackJob struct {
*EthCallbackJob
}
3 changes: 3 additions & 0 deletions task/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const (
WITHDRAWAL_TASK = "withdrawal"
VOTE_BRIDGE_OPERATORS_TASK = "voteBridgeOperatorsBySignatures"
RELAY_BRIDGE_OPERATORS_TASK = "relayBridgeOperators"
BRIDGE_SYNC_REWARD_TASK = "bridgeSyncReward"

STATUS_PENDING = "pending"
STATUS_FAILED = "failed"
Expand All @@ -20,6 +21,8 @@ const (
ETH_GOVERNANCE_CONTRACT = "ethgovernance"
ETH_GATEWAY_CONTRACT = "ethgateway"
BRIDGEADMIN_CONTRACT = "bridgeadmin"
BRIDGE_REWARD_CONTRACT = "bridgereward"
RONIN_VALIDATOR_SET_CONTRACT = "roninvalidatorset"
)

const (
Expand Down
25 changes: 25 additions & 0 deletions task/ronin.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ func (r *RoninTask) processPending() error {
if operatorTask := r.collectOperatorsTask(t); operatorTask != nil {
singleTasks = append(singleTasks, operatorTask)
}

if syncRewardTask := r.collectSyncRewardTask(t); syncRewardTask != nil {
singleTasks = append(singleTasks, syncRewardTask)
}
}
bulkDepositTask.send()
bulkSubmitWithdrawalSignaturesTask.send()
Expand Down Expand Up @@ -291,6 +295,27 @@ func (r *RoninTask) collectOperatorsTask(task *models.Task) Tasker {
return nil
}

func (r *RoninTask) collectSyncRewardTask(task *models.Task) Tasker {
if task.Type == BRIDGE_SYNC_REWARD_TASK {
syncRewardTask := newTask(
r.listener,
r.roninRpcClient,
nil,
r.store,
r.chainId,
r.contracts,
defaultMaxTry,
task.Type,
r.releaseTasksCh,
r.util,
r.gasLimitBumpRatio,
)
syncRewardTask.collectTask(task)
return syncRewardTask
}
return nil
}

func (r *RoninTask) lockTask(t *models.Task) {
if t != nil {
r.processingIdsMap.Store(t.ID, struct{}{})
Expand Down
27 changes: 27 additions & 0 deletions task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
"time"

bridgeReward "github.com/axieinfinity/bridge-v2/generated_contracts/bridge_reward"
"github.com/axieinfinity/bridge-v2/stats"

bridgeCore "github.com/axieinfinity/bridge-core"
Expand Down Expand Up @@ -92,6 +93,8 @@ func (r *task) send() {
r.sendTransaction(r.voteBridgeOperatorsBySignature)
case RELAY_BRIDGE_OPERATORS_TASK:
r.sendTransaction(r.relayBridgeOperators)
case BRIDGE_SYNC_REWARD_TASK:
r.sendTransaction(r.syncReward)
}
}

Expand All @@ -115,6 +118,30 @@ func (r *task) sendTransaction(sendTx func(task *models.Task) (doneTasks, proces
_ = metrics.Pusher.IncrCounter(metrics.FailedTaskMetric, len(failedTasks))
}

func (r *task) handleTaskError(task *models.Task, failedTasks *[]*models.Task, err error) {
task.LastError = err.Error()
stats.SendErrorToStats(r.listener, err)
*failedTasks = append(*failedTasks, task)
}

func (r *task) syncReward(task *models.Task) (doneTasks, processingTasks, failedTasks []*models.Task, tx *ethtypes.Transaction) {
bridgeRewardContract, err := bridgeReward.NewBridgeReward(common.HexToAddress(r.contracts[BRIDGE_REWARD_CONTRACT]), r.client)
if err != nil {
r.handleTaskError(task, &failedTasks, err)
return nil, nil, failedTasks, nil
}

tx, err = r.util.SendContractTransaction(r.listener.GetBridgeOperatorSign(), r.chainId, r.gasLimitBumpRatio, func(opts *bind.TransactOpts) (*ethtypes.Transaction, error) {
return bridgeRewardContract.SyncRewardManual(opts, common.Big0)
})
if err != nil {
r.handleTaskError(task, &failedTasks, err)
return nil, nil, failedTasks, nil
}
processingTasks = append(processingTasks, task)
return
}

func (r *task) voteBridgeOperatorsBySignature(task *models.Task) (doneTasks, processingTasks, failedTasks []*models.Task, tx *ethtypes.Transaction) {
log.Info("[RoninTask][BridgeOperatorSetCallback] Processing task")

Expand Down

0 comments on commit e66460b

Please sign in to comment.