wormhole/bridge/pkg/ethereum/watcher.go

155 lines
4.0 KiB
Go
Raw Normal View History

2020-08-06 06:43:09 -07:00
package ethereum
import (
"context"
"fmt"
"github.com/certusone/wormhole/bridge/pkg/common"
"github.com/certusone/wormhole/bridge/pkg/ethereum/abi"
"github.com/certusone/wormhole/bridge/pkg/supervisor"
"github.com/certusone/wormhole/bridge/pkg/vaa"
2020-08-06 10:00:16 -07:00
"github.com/ethereum/go-ethereum/accounts/abi/bind"
2020-08-06 06:43:09 -07:00
eth_common "github.com/ethereum/go-ethereum/common"
2020-08-06 10:00:16 -07:00
"github.com/ethereum/go-ethereum/core/types"
2020-08-06 06:43:09 -07:00
"github.com/ethereum/go-ethereum/ethclient"
2020-08-06 10:00:16 -07:00
"go.uber.org/zap"
2020-08-06 06:43:09 -07:00
"sync"
2020-08-06 10:00:16 -07:00
"time"
2020-08-06 06:43:09 -07:00
)
type (
EthBridgeWatcher struct {
2020-08-06 10:00:16 -07:00
url string
bridge eth_common.Address
minConfirmations uint64
pendingLocks map[eth_common.Hash]*pendingLock
pendingLocksGuard sync.Mutex
2020-08-06 06:43:09 -07:00
evChan chan *common.ChainLock
}
2020-08-06 10:00:16 -07:00
pendingLock struct {
lock *common.ChainLock
txHash eth_common.Hash
height uint64
}
2020-08-06 06:43:09 -07:00
)
2020-08-06 10:00:16 -07:00
func NewEthBridgeWatcher(url string, bridge eth_common.Address, minConfirmations uint64, events chan *common.ChainLock) *EthBridgeWatcher {
return &EthBridgeWatcher{url: url, bridge: bridge, minConfirmations: minConfirmations, evChan: events, pendingLocks: map[eth_common.Hash]*pendingLock{}}
2020-08-06 06:43:09 -07:00
}
func (e *EthBridgeWatcher) Run(ctx context.Context) error {
2020-08-17 05:56:22 -07:00
c, err := ethclient.DialContext(ctx, e.url)
2020-08-06 06:43:09 -07:00
if err != nil {
return fmt.Errorf("dialing eth client failed: %w", err)
}
f, err := abi.NewWormholeBridgeFilterer(e.bridge, c)
if err != nil {
return fmt.Errorf("could not create wormhole bridge filter: %w", err)
}
2020-08-06 10:00:16 -07:00
sink := make(chan *abi.WormholeBridgeLogTokensLocked, 2)
eventSubscription, err := f.WatchLogTokensLocked(&bind.WatchOpts{
Context: ctx,
}, sink, nil, nil)
2020-08-06 06:43:09 -07:00
if err != nil {
return fmt.Errorf("failed to subscribe to eth events: %w", err)
}
2020-08-06 10:00:16 -07:00
defer eventSubscription.Unsubscribe()
2020-08-06 06:43:09 -07:00
2020-08-17 05:56:22 -07:00
errC := make(chan error)
logger := supervisor.Logger(ctx)
2020-08-06 06:43:09 -07:00
go func() {
for {
select {
2020-08-17 05:56:22 -07:00
case <-ctx.Done():
return
2020-08-06 10:00:16 -07:00
case e := <-eventSubscription.Err():
2020-08-17 05:56:22 -07:00
errC <- e
2020-08-06 06:43:09 -07:00
return
case ev := <-sink:
lock := &common.ChainLock{
SourceAddress: ev.Sender,
TargetAddress: ev.Recipient,
SourceChain: vaa.ChainIDEthereum,
TargetChain: vaa.ChainID(ev.TargetChain),
TokenChain: vaa.ChainID(ev.TokenChain),
TokenAddress: ev.Token,
Amount: ev.Amount,
}
2020-08-06 10:00:16 -07:00
2020-08-17 05:56:22 -07:00
logger.Info("found new lockup transaction", zap.Stringer("tx", ev.Raw.TxHash),
2020-08-06 10:00:16 -07:00
zap.Uint64("number", ev.Raw.BlockNumber))
e.pendingLocksGuard.Lock()
e.pendingLocks[ev.Raw.TxHash] = &pendingLock{
lock: lock,
txHash: ev.Raw.TxHash,
height: ev.Raw.BlockNumber,
}
e.pendingLocksGuard.Unlock()
2020-08-06 06:43:09 -07:00
}
}
2020-08-06 10:00:16 -07:00
}()
// Watch headers
headSink := make(chan *types.Header, 2)
headerSubscription, err := c.SubscribeNewHead(ctx, headSink)
if err != nil {
return fmt.Errorf("failed to subscribe to header events: %w", err)
}
defer headerSubscription.Unsubscribe()
2020-08-06 06:43:09 -07:00
2020-08-06 10:00:16 -07:00
go func() {
for {
select {
2020-08-17 05:56:22 -07:00
case <-ctx.Done():
return
2020-08-06 10:00:16 -07:00
case e := <-headerSubscription.Err():
2020-08-17 05:56:22 -07:00
errC <- e
2020-08-06 10:00:16 -07:00
return
case ev := <-headSink:
start := time.Now()
2020-08-17 05:56:22 -07:00
logger.Info("processing new header", zap.Stringer("number", ev.Number))
2020-08-06 10:00:16 -07:00
e.pendingLocksGuard.Lock()
blockNumberU := ev.Number.Uint64()
for hash, pLock := range e.pendingLocks {
// Transaction was dropped and never picked up again
if pLock.height+4*e.minConfirmations <= blockNumberU {
2020-08-17 05:56:22 -07:00
logger.Debug("lockup timed out", zap.Stringer("tx", pLock.txHash),
2020-08-06 10:00:16 -07:00
zap.Stringer("number", ev.Number))
delete(e.pendingLocks, hash)
continue
}
// Transaction is now ready
if pLock.height+e.minConfirmations <= ev.Number.Uint64() {
2020-08-17 05:56:22 -07:00
logger.Debug("lockup confirmed", zap.Stringer("tx", pLock.txHash),
2020-08-06 10:00:16 -07:00
zap.Stringer("number", ev.Number))
delete(e.pendingLocks, hash)
e.evChan <- pLock.lock
}
}
e.pendingLocksGuard.Unlock()
2020-08-17 05:56:22 -07:00
logger.Info("processed new header", zap.Stringer("number", ev.Number),
2020-08-06 10:00:16 -07:00
zap.Duration("took", time.Since(start)))
}
}
2020-08-06 06:43:09 -07:00
}()
supervisor.Signal(ctx, supervisor.SignalHealthy)
2020-08-17 05:56:22 -07:00
select {
case <-ctx.Done():
return ctx.Err()
case err := <-errC:
return err
}
2020-08-06 06:43:09 -07:00
}