2020-08-06 06:43:09 -07:00
|
|
|
package ethereum
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2020-08-17 10:29:25 -07:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
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-17 10:29:25 -07:00
|
|
|
|
|
|
|
"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 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
|
|
|
|
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{
|
2020-08-17 10:29:25 -07:00
|
|
|
TxHash: ev.Raw.TxHash,
|
2020-08-06 06:43:09 -07:00
|
|
|
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,
|
|
|
|
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 10:29:25 -07:00
|
|
|
logger.Debug("lockup timed out", zap.Stringer("tx", pLock.lock.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 10:29:25 -07:00
|
|
|
logger.Debug("lockup confirmed", zap.Stringer("tx", pLock.lock.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
|
|
|
}
|