Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions app/arbiter/arbiter/arbiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"encoding/gob"
"encoding/hex"
"log"
"math/big"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -95,6 +96,7 @@ func NewArbiter(ctx context.Context, config *config.Config, password string) *Ar
func (v *Arbiter) Start() {
if v.config.Signer {
go v.processArbiterSig()
go v.processManualConfirm()
}

if v.config.Listener {
Expand All @@ -113,6 +115,124 @@ func (v *Arbiter) listenESCContract() {
v.escNode.Start(startHeight)
}

func (v *Arbiter) processManualConfirm() {
g.Log().Info(v.ctx, "process manually confirm start")

for {
// get all deploy file
files, err := os.ReadDir(v.config.LoanManuallyConfirmedPath)
if err != nil {
g.Log().Error(v.ctx, "read dir error", err)
continue
}

for _, file := range files {
// read file
filePath := v.config.LoanManuallyConfirmedPath + "/" + file.Name()
fileContent, err := os.ReadFile(filePath)
if err != nil {
g.Log().Error(v.ctx, "read file error", err)
continue
}
logEvt, err := v.decodeLogEvtByFileContent(fileContent)
if err != nil {
g.Log().Error(v.ctx, "decodeLogEvtByFileContent error", err)
v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".mcFailed")
v.logger.Println("[ERR] MCFM: decode event failed, file:", filePath)
continue
}
var ev = make(map[string]interface{})
err = v.escNode.Order_abi.UnpackIntoMap(ev, "ConfirmTransferToLenderEvent", logEvt.EventData)
if err != nil {
g.Log().Error(v.ctx, "UnpackIntoMap error", err)
v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".mcFailed")
v.logger.Println("[ERR] MCFM: unpack event into map failed, file:", filePath)
continue
}
g.Log().Info(v.ctx, "ev", ev)
orderId := logEvt.Topics[1]
btcTxHash := logEvt.Topics[2]
arbiterAddresss := common.BytesToAddress(logEvt.Topics[3][:])
fee := ev["arbitratorBtcFee"].(*big.Int)

g.Log().Info(v.ctx, "orderId", hex.EncodeToString(orderId[:]))
g.Log().Info(v.ctx, "btcTxHash", hex.EncodeToString(btcTxHash[:]))
g.Log().Info(v.ctx, "arbiterAddresss", arbiterAddresss.String())

// get btc arbiter BTC address
arbitratorBTCAddress, err := v.escNode.GetArbiterBTCAddress(arbiterAddresss)
if err != nil {
g.Log().Error(v.ctx, "GetArbiterOperatorAddress error", err)
v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".mcGetArbiterOperatorAddressFailed")
v.logger.Println("[ERR] MCFM: get arbiter operator address failed, block:", logEvt.Block, "tx:", logEvt.TxHash)
continue
}

btcTx, err := v.mempoolAPI.GetRawTransaction(hex.EncodeToString(btcTxHash[:]))
if err != nil {
g.Log().Error(v.ctx, "GetRawTransaction error", err)
// v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".GetRawTransactionFailed")
v.logger.Println("[ERR] MCFM: get raw tx failed, block:", logEvt.Block, "tx:", logEvt.TxHash)
continue
}
// check if have enough fee
realFee := int64(0)
// feeOutputIndex := 0
for _, vout := range btcTx.Vout {
if vout.Value < 546 {
g.Log().Error(v.ctx, "invalid tx outputs with dust value")
v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".mcInvalidTxOutputs")
v.logger.Println("[ERR] MCFM: invalid tx outputs with dust value, block:", logEvt.Block, "tx:", logEvt.TxHash)
continue
}
utxoAddr := vout.ScriptpubkeyAddress
if utxoAddr == arbitratorBTCAddress {
g.Log().Error(v.ctx, "invalid utxo address:", utxoAddr, "need to be:", arbitratorBTCAddress)
v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".mcInvalidUtxoAddress")
v.logger.Println("[ERR] MCFM: invalid utxo address, block:", logEvt.Block, "tx:", logEvt.TxHash)
continue
}
if vout.Value > 0 {
realFee += vout.Value
// feeOutputIndex = i
break
}
}
// check fee rate
// preAmount := int64(btcTx.Vout[1-feeOutputIndex].Value)
// feeRate, err := v.escNode.GetManuallyConfirmedBTCFeeRate(&arbiterAddresss)
// if err != nil {
// g.Log().Error(v.ctx, "GetManuallyConfirmedBTCFeeRate error", err)
// v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".mcGetManuallyConfirmedBTCFeeRate")
// v.logger.Println("[ERR] MCFM: get fee rate failed, block:", logEvt.Block, "tx:", logEvt.TxHash)
// continue
// }
// arbiterFee := preAmount * feeRate.Int64() / 10000
if realFee < fee.Int64() {
g.Log().Error(v.ctx, "invalid fee:", realFee, "need to be:", fee.Int64())
v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".mcInvalidFeeRate")
v.logger.Println("[ERR] MCFM: invalid fee rate, block:", logEvt.Block, "tx:", logEvt.TxHash)
continue
}

// manually confirm to contract
orderContarctAddress := common.BytesToAddress(orderId[:])
txhash, err := v.escNode.SubmitManuallyConfirm(&orderContarctAddress)
g.Log().Notice(v.ctx, "SubmitManuallyConfirmed", "txhash ", txhash.String(), " error ", err)
if err != nil {
v.moveToDirectory(filePath, v.config.LoanNeedSignFailedPath+"/"+file.Name()+".mcSubmitFailed")
v.logger.Println("[ERR] MCFM: SubmitManuallyConfirm failed, block:", logEvt.Block, "tx:", logEvt.TxHash, "err:", err.Error())
} else {
v.moveToDirectory(filePath, v.config.LoanNeedSignSignedPath+"/"+file.Name()+".mcSucceed")
v.logger.Println("[INF] MCFM: SubmitManuallyConfirmed succeed, block:", logEvt.Block, "tx:", logEvt.TxHash)
}
}

// sleep 10s to check and process next files
time.Sleep(time.Second * 10)
}
}

func (v *Arbiter) processArbiterSig() {
g.Log().Info(v.ctx, "processArbiterSignature start")

Expand Down Expand Up @@ -387,6 +507,20 @@ func createDir(config *config.Config) error {
}
}

if !gfile.Exists(config.LoanSignedEventPath) {
err := gfile.Mkdir(config.LoanSignedEventPath)
if err != nil {
return err
}
}

if !gfile.Exists(config.LoanManuallyConfirmedPath) {
err := gfile.Mkdir(config.LoanManuallyConfirmedPath)
if err != nil {
return err
}
}

if !gfile.Exists(config.LoanLogPath) {
err := gfile.Mkdir(config.LoanLogPath)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions app/arbiter/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Config struct {
ESCArbiterContractAddress string
ESCArbiterManagerContractAddress string
ESCConfigManagerContractAddress string
ESCOrderManagerContractAddress string
ESCArbiterAddress string

DataDir string
Expand All @@ -27,6 +28,8 @@ type Config struct {
LoanNeedSignFailedPath string
// loan signed path
LoanNeedSignSignedPath string
// loan manually confirmed path
LoanManuallyConfirmedPath string
// loan logs path
LoanLogPath string

Expand Down
26 changes: 26 additions & 0 deletions app/arbiter/contract/contract_abi/arbiterManagerABI.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,32 @@
package contract_abi

const ArbiterManagerABI = `[
{
"inputs": [
{
"internalType": "address",
"name": "arbitrator",
"type": "address"
}
],
"name": "getArbitratorInfoExt",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "currentBTCFeeRate",
"type": "uint256"
}
],
"internalType": "struct DataTypes.ArbitratorInfoExt",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"stateMutability": "nonpayable",
Expand Down
50 changes: 50 additions & 0 deletions app/arbiter/contract/contract_abi/orderManagerABI.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2025 The bel2 developers

package contract_abi

const OrderEventManagerABI = `[
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "order",
"type": "address"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "txId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "arbitrator",
"type": "address"
},
{
"indexed": false,
"internalType": "uint32",
"name": "txIndex",
"type": "uint32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "arbitratorBtcFee",
"type": "uint256"
}
],
"name": "ConfirmTransferToLenderEvent",
"type": "event"
},
{
"inputs": [],
"name": "confirmTransferToArbitrator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]`
17 changes: 11 additions & 6 deletions app/arbiter/contract/contract_listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,22 @@ import (

type ContractListener struct {
loanContract common.Address
orderContract common.Address

queryClient *CrossClient
listeneTopics []common.Hash
ctx context.Context
chan_events chan *events.ContractLogEvent
}

func NewListener(ctx context.Context, client *CrossClient,
loanContract common.Address, chan_event chan *events.ContractLogEvent) (*ContractListener, error) {
loanContract, orderContract common.Address, chan_event chan *events.ContractLogEvent) (*ContractListener, error) {
c := &ContractListener{
queryClient: client,
loanContract: loanContract,
ctx: ctx,
chan_events: chan_event,
queryClient: client,
loanContract: loanContract,
orderContract: orderContract,
ctx: ctx,
chan_events: chan_event,
}
c.listeneTopics = make([]common.Hash, 0)
return c, nil
Expand All @@ -48,7 +51,9 @@ func (c *ContractListener) Start(startHeight uint64) (uint64, error) {

distance := uint64(10000)
toBlock := startHeight
loanQuery := c.queryClient.BuildQuery(c.loanContract, c.listeneTopics, nil, nil)
// addresses := []common.Address{c.orderContract}
addresses := []common.Address{c.loanContract, c.orderContract}
loanQuery := c.queryClient.BuildQuery(addresses, c.listeneTopics, nil, nil)
for i := startHeight; i <= endBlock-confirmBlocksCount; i += distance {
if i+distance < endBlock {
toBlock = i + distance
Expand Down
Loading