Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(relayer): Wait N confirmations on source chain before processing message on destination chain #270

Merged
merged 13 commits into from
Nov 16, 2022
Merged
3 changes: 2 additions & 1 deletion packages/relayer/.default.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ RELAYER_ECDSA_KEY=
L1_BRIDGE_ADDRESS=0xa566811E9E63e4F573Df89d5453bB89F239F7e10
L2_BRIDGE_ADDRESS=0xa566811E9E63e4F573Df89d5453bB89F239F7e10
L1_RPC_URL="wss://eth-goerli.g.alchemy.com/v2/bPAA5rQ42Zoo4ts9TYnTB2t0cuc5lf7_"
L2_RPC_URL="wss://rinkeby-light.eth.linkpool.io/ws"
L2_RPC_URL="wss://rinkeby-light.eth.linkpool.io/ws"
CONFIRMATIONS_BEFORE_PROCESSING=13
cyberhorsey marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion packages/relayer/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ linters:

linters-settings:
funlen:
lines: 100
lines: 105
statements: 45
gocognit:
min-complexity: 32
Expand Down
11 changes: 11 additions & 0 deletions packages/relayer/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"strconv"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
Expand Down Expand Up @@ -32,6 +33,7 @@ var (
"MYSQL_DATABASE",
"MYSQL_HOST",
"RELAYER_ECDSA_KEY",
"CONFIRMATIONS_BEFORE_PROCESSING",
}
)

Expand Down Expand Up @@ -108,6 +110,11 @@ func makeIndexers(layer Layer, db *gorm.DB) ([]*indexer.Service, func(), error)
return nil, nil, err
}

confirmations, err := strconv.Atoi(os.Getenv("CONFIRMATIONS_BEFORE_PROCESSING"))
cyberhorsey marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, nil, err
cyberhorsey marked this conversation as resolved.
Show resolved Hide resolved
}

indexers := make([]*indexer.Service, 0)

if layer == L1 || layer == Both {
Expand All @@ -123,6 +130,8 @@ func makeIndexers(layer Layer, db *gorm.DB) ([]*indexer.Service, func(), error)
BridgeAddress: common.HexToAddress(os.Getenv("L1_BRIDGE_ADDRESS")),
DestBridgeAddress: common.HexToAddress(os.Getenv("L2_BRIDGE_ADDRESS")),
DestTaikoAddress: common.HexToAddress(os.Getenv("L2_TAIKO_ADDRESS")),

Confirmations: uint64(confirmations),
})
if err != nil {
log.Fatal(err)
Expand All @@ -144,6 +153,8 @@ func makeIndexers(layer Layer, db *gorm.DB) ([]*indexer.Service, func(), error)
BridgeAddress: common.HexToAddress(os.Getenv("L2_BRIDGE_ADDRESS")),
DestBridgeAddress: common.HexToAddress(os.Getenv("L1_BRIDGE_ADDRESS")),
DestTaikoAddress: common.HexToAddress(os.Getenv("L1_TAIKO_ADDRESS")),

Confirmations: uint64(confirmations),
})
if err != nil {
log.Fatal(err)
Expand Down
5 changes: 5 additions & 0 deletions packages/relayer/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,9 @@ var (
ErrNoRPCClient = errors.Validation.NewWithKeyAndDetail("ERR_NO_RPC_CLIENT", "RPCClient is required")
ErrNoBridge = errors.Validation.NewWithKeyAndDetail("ERR_NO_BRIDGE", "Bridge is required")
ErrNoTaikoL2 = errors.Validation.NewWithKeyAndDetail("ERR_NO_TAIKO_L2", "TaikoL2 is required")

ErrInvalidConfirmations = errors.Validation.NewWithKeyAndDetail(
"ERR_INVALID_CONFIRMATIONS",
"Confirmations amount is invalid, must be numerical and > 0",
)
)
3 changes: 3 additions & 0 deletions packages/relayer/indexer/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type NewServiceOpts struct {
BridgeAddress common.Address
DestBridgeAddress common.Address
DestTaikoAddress common.Address
Confirmations uint64
}

func NewService(opts NewServiceOpts) (*Service, error) {
Expand Down Expand Up @@ -122,6 +123,8 @@ func NewService(opts NewServiceOpts) (*Service, error) {
DestBridge: destBridge,
EventRepo: opts.EventRepo,
DestHeaderSyncer: destHeaderSyncer,
Confirmations: opts.Confirmations,
SrcETHClient: opts.EthClient,
})
if err != nil {
return nil, errors.Wrap(err, "message.NewProcessor")
Expand Down
9 changes: 9 additions & 0 deletions packages/relayer/indexer/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func Test_NewService(t *testing.T) {
ECDSAKey: dummyEcdsaKey,
BridgeAddress: common.HexToAddress(dummyAddress),
DestBridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
nil,
},
Expand All @@ -44,6 +45,7 @@ func Test_NewService(t *testing.T) {
ECDSAKey: dummyEcdsaKey,
BridgeAddress: common.HexToAddress(dummyAddress),
DestBridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
relayer.ErrNoRPCClient,
},
Expand All @@ -57,6 +59,7 @@ func Test_NewService(t *testing.T) {
ECDSAKey: dummyEcdsaKey,
RPCClient: &rpc.Client{},
DestBridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
relayer.ErrNoBridgeAddress,
},
Expand All @@ -70,6 +73,7 @@ func Test_NewService(t *testing.T) {
ECDSAKey: dummyEcdsaKey,
RPCClient: &rpc.Client{},
BridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
relayer.ErrNoBridgeAddress,
},
Expand All @@ -83,6 +87,7 @@ func Test_NewService(t *testing.T) {
DestEthClient: &ethclient.Client{},
BridgeAddress: common.HexToAddress(dummyAddress),
DestBridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
relayer.ErrNoECDSAKey,
},
Expand All @@ -96,6 +101,7 @@ func Test_NewService(t *testing.T) {
BridgeAddress: common.HexToAddress(dummyAddress),
RPCClient: &rpc.Client{},
DestBridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
relayer.ErrNoEventRepository,
},
Expand All @@ -109,6 +115,7 @@ func Test_NewService(t *testing.T) {
DestEthClient: &ethclient.Client{},
BridgeAddress: common.HexToAddress(dummyAddress),
DestBridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
relayer.ErrNoBlockRepository,
},
Expand All @@ -122,6 +129,7 @@ func Test_NewService(t *testing.T) {
DestEthClient: &ethclient.Client{},
BridgeAddress: common.HexToAddress(dummyAddress),
DestBridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
relayer.ErrNoEthClient,
},
Expand All @@ -135,6 +143,7 @@ func Test_NewService(t *testing.T) {
RPCClient: &rpc.Client{},
BridgeAddress: common.HexToAddress(dummyAddress),
DestBridgeAddress: common.HexToAddress(dummyAddress),
Confirmations: 1,
},
relayer.ErrNoEthClient,
},
Expand Down
23 changes: 23 additions & 0 deletions packages/relayer/message/process_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/hex"
"math/big"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -29,6 +30,10 @@ func (p *Processor) ProcessMessage(
return errors.New("only user can process this, gasLimit set to 0")
}

if err := p.waitForConfirmations(ctx, event.Raw.BlockNumber); err != nil {
return errors.Wrap(err, "p.waitForConfirmations")
}

// get latest synced header since not every header is synced from L1 => L2,
// and later blocks still have the storage trie proof from previous blocks.
latestSyncedHeader, err := p.destHeaderSyncer.GetLatestSyncedHeader(&bind.CallOpts{})
Expand Down Expand Up @@ -116,3 +121,21 @@ func (p *Processor) ProcessMessage(

return nil
}

func (p *Processor) waitForConfirmations(ctx context.Context, blockNumber uint64) error {
// TODO: make timeout a config var
ctx, cancelFunc := context.WithTimeout(ctx, 2*time.Minute)

defer cancelFunc()

if err := relayer.WaitConfirmations(
ctx,
p.srcEthClient,
p.confirmations,
blockNumber,
); err != nil {
return errors.Wrap(err, "relayer.WaitConfirmations")
}

return nil
}
26 changes: 22 additions & 4 deletions packages/relayer/message/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

type Processor struct {
eventRepo relayer.EventRepository
srcEthClient *ethclient.Client
destEthClient *ethclient.Client
rpc *rpc.Client
ecdsaKey *ecdsa.PrivateKey
Expand All @@ -21,16 +22,20 @@ type Processor struct {
destHeaderSyncer *contracts.IHeaderSync

prover *proof.Prover

confirmations uint64
}

type NewProcessorOpts struct {
Prover *proof.Prover
ECDSAKey *ecdsa.PrivateKey
RPCClient *rpc.Client
SrcETHClient *ethclient.Client
DestETHClient *ethclient.Client
DestBridge *contracts.Bridge
EventRepo relayer.EventRepository
DestHeaderSyncer *contracts.IHeaderSync
Confirmations uint64
}

func NewProcessor(opts NewProcessorOpts) (*Processor, error) {
Expand All @@ -50,6 +55,10 @@ func NewProcessor(opts NewProcessorOpts) (*Processor, error) {
return nil, relayer.ErrNoEthClient
}

if opts.SrcETHClient == nil {
return nil, relayer.ErrNoEthClient
}

if opts.DestBridge == nil {
return nil, relayer.ErrNoBridge
}
Expand All @@ -62,13 +71,22 @@ func NewProcessor(opts NewProcessorOpts) (*Processor, error) {
return nil, relayer.ErrNoTaikoL2
}

if opts.Confirmations == 0 {
return nil, relayer.ErrInvalidConfirmations
}

return &Processor{
eventRepo: opts.EventRepo,
prover: opts.Prover,
ecdsaKey: opts.ECDSAKey,
rpc: opts.RPCClient,
eventRepo: opts.EventRepo,
prover: opts.Prover,
ecdsaKey: opts.ECDSAKey,
rpc: opts.RPCClient,

srcEthClient: opts.SrcETHClient,

destEthClient: opts.DestETHClient,
destBridge: opts.DestBridge,
destHeaderSyncer: opts.DestHeaderSyncer,

confirmations: opts.Confirmations,
}, nil
}
44 changes: 44 additions & 0 deletions packages/relayer/message/processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,53 @@ func Test_NewProcessor(t *testing.T) {
Prover: &proof.Prover{},
ECDSAKey: &ecdsa.PrivateKey{},
RPCClient: &rpc.Client{},
SrcETHClient: &ethclient.Client{},
DestETHClient: &ethclient.Client{},
DestBridge: &contracts.Bridge{},
EventRepo: &repo.EventRepository{},
DestHeaderSyncer: &contracts.IHeaderSync{},
Confirmations: 1,
},
nil,
},
{
"errNoConfirmations",
NewProcessorOpts{
Prover: &proof.Prover{},
ECDSAKey: &ecdsa.PrivateKey{},
RPCClient: &rpc.Client{},
SrcETHClient: &ethclient.Client{},
DestETHClient: &ethclient.Client{},
DestBridge: &contracts.Bridge{},
EventRepo: &repo.EventRepository{},
DestHeaderSyncer: &contracts.IHeaderSync{},
},
relayer.ErrInvalidConfirmations,
},
{
"errNoSrcClient",
NewProcessorOpts{
Prover: &proof.Prover{},
ECDSAKey: &ecdsa.PrivateKey{},
RPCClient: &rpc.Client{},
DestETHClient: &ethclient.Client{},
DestBridge: &contracts.Bridge{},
EventRepo: &repo.EventRepository{},
DestHeaderSyncer: &contracts.IHeaderSync{},
Confirmations: 1,
},
relayer.ErrNoEthClient,
},
{
"errNoProver",
NewProcessorOpts{
ECDSAKey: &ecdsa.PrivateKey{},
RPCClient: &rpc.Client{},
SrcETHClient: &ethclient.Client{},
DestETHClient: &ethclient.Client{},
DestBridge: &contracts.Bridge{},
EventRepo: &repo.EventRepository{},
Confirmations: 1,
DestHeaderSyncer: &contracts.IHeaderSync{},
},
relayer.ErrNoProver,
Expand All @@ -50,10 +82,12 @@ func Test_NewProcessor(t *testing.T) {
Prover: &proof.Prover{},

RPCClient: &rpc.Client{},
SrcETHClient: &ethclient.Client{},
DestETHClient: &ethclient.Client{},
DestBridge: &contracts.Bridge{},
EventRepo: &repo.EventRepository{},
DestHeaderSyncer: &contracts.IHeaderSync{},
Confirmations: 1,
},
relayer.ErrNoECDSAKey,
},
Expand All @@ -62,10 +96,12 @@ func Test_NewProcessor(t *testing.T) {
NewProcessorOpts{
Prover: &proof.Prover{},
ECDSAKey: &ecdsa.PrivateKey{},
SrcETHClient: &ethclient.Client{},
DestETHClient: &ethclient.Client{},
DestBridge: &contracts.Bridge{},
EventRepo: &repo.EventRepository{},
DestHeaderSyncer: &contracts.IHeaderSync{},
Confirmations: 1,
},
relayer.ErrNoRPCClient,
},
Expand All @@ -75,9 +111,11 @@ func Test_NewProcessor(t *testing.T) {
Prover: &proof.Prover{},
ECDSAKey: &ecdsa.PrivateKey{},
RPCClient: &rpc.Client{},
SrcETHClient: &ethclient.Client{},
DestBridge: &contracts.Bridge{},
EventRepo: &repo.EventRepository{},
DestHeaderSyncer: &contracts.IHeaderSync{},
Confirmations: 1,
},
relayer.ErrNoEthClient,
},
Expand All @@ -87,9 +125,11 @@ func Test_NewProcessor(t *testing.T) {
Prover: &proof.Prover{},
ECDSAKey: &ecdsa.PrivateKey{},
RPCClient: &rpc.Client{},
SrcETHClient: &ethclient.Client{},
DestETHClient: &ethclient.Client{},
EventRepo: &repo.EventRepository{},
DestHeaderSyncer: &contracts.IHeaderSync{},
Confirmations: 1,
},
relayer.ErrNoBridge,
},
Expand All @@ -99,9 +139,11 @@ func Test_NewProcessor(t *testing.T) {
Prover: &proof.Prover{},
ECDSAKey: &ecdsa.PrivateKey{},
RPCClient: &rpc.Client{},
SrcETHClient: &ethclient.Client{},
DestETHClient: &ethclient.Client{},
DestBridge: &contracts.Bridge{},
DestHeaderSyncer: &contracts.IHeaderSync{},
Confirmations: 1,
},
relayer.ErrNoEventRepository,
},
Expand All @@ -111,9 +153,11 @@ func Test_NewProcessor(t *testing.T) {
Prover: &proof.Prover{},
ECDSAKey: &ecdsa.PrivateKey{},
RPCClient: &rpc.Client{},
SrcETHClient: &ethclient.Client{},
DestETHClient: &ethclient.Client{},
EventRepo: &repo.EventRepository{},
DestBridge: &contracts.Bridge{},
Confirmations: 1,
},
relayer.ErrNoTaikoL2,
},
Expand Down
Loading