Node: EVM watcher tests, phase1 (#1915)
* Node: EVM watcher tests, phase1 Change-Id: I8bbcebc52e3a4e70dda8ea5b53001a2b913c7f9a * Update optimism tests to match latest code Change-Id: Ib19c815e80d821c2df7ffc416ab3b8b6f2b91b18 * Clean up poller test Change-Id: I4176ceacefe781d22174e91815309188b31ccf6a * Node: rework for evm watcher tests Change-Id: Idc7bbe4a95402e8af536cef53a4dd2daaa181de1 * Node: More evm watcher test rework Change-Id: Ibb6466cf425b07bb771a51a3d2131bce73e0dd97 * Node: fix lint errors in evm watcher tests Change-Id: I6d62cb35c43b20d36c72824fb17a0b4c912fe504 * Node: Fix lint errors in evm tests Change-Id: Iadd08bd294dc2c6206e04217ef06836507edf1ec
This commit is contained in:
parent
0872f4ff16
commit
f41eebe0b3
|
@ -42,16 +42,20 @@ func NewBlockPollConnector(ctx context.Context, baseConnector Connector, finaliz
|
|||
publishSafeBlocks: publishSafeBlocks,
|
||||
finalizer: finalizer,
|
||||
}
|
||||
err := supervisor.Run(ctx, "blockPoller", connector.run)
|
||||
err := supervisor.Run(ctx, "blockPoller", connector.runFromSupervisor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connector, nil
|
||||
}
|
||||
|
||||
func (b *BlockPollConnector) run(ctx context.Context) error {
|
||||
func (b *BlockPollConnector) runFromSupervisor(ctx context.Context) error {
|
||||
logger := supervisor.Logger(ctx).With(zap.String("eth_network", b.Connector.NetworkName()))
|
||||
supervisor.Signal(ctx, supervisor.SignalHealthy)
|
||||
return b.run(ctx, logger)
|
||||
}
|
||||
|
||||
func (b *BlockPollConnector) run(ctx context.Context, logger *zap.Logger) error {
|
||||
lastBlock, err := b.getBlock(ctx, logger, nil, false)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -66,7 +70,6 @@ func (b *BlockPollConnector) run(ctx context.Context) error {
|
|||
}
|
||||
|
||||
timer := time.NewTimer(time.Millisecond) // Start immediately.
|
||||
supervisor.Signal(ctx, supervisor.SignalHealthy)
|
||||
|
||||
for {
|
||||
select {
|
||||
|
|
|
@ -0,0 +1,350 @@
|
|||
package connectors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
ethAbi "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/ethabi"
|
||||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethClient "github.com/ethereum/go-ethereum/ethclient"
|
||||
ethEvent "github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
// mockConnectorForPoller implements the connector interface for testing purposes.
|
||||
type mockConnectorForPoller struct {
|
||||
address ethCommon.Address
|
||||
client *ethClient.Client
|
||||
mutex sync.Mutex
|
||||
err error
|
||||
persistentError bool
|
||||
blockNumber uint64
|
||||
}
|
||||
|
||||
// setError takes an error which will be returned on the next RPC call. The error will persist until cleared.
|
||||
func (m *mockConnectorForPoller) setError(err error) {
|
||||
m.mutex.Lock()
|
||||
m.err = err
|
||||
m.persistentError = true
|
||||
m.mutex.Unlock()
|
||||
}
|
||||
|
||||
// setSingleError takes an error which will be returned on the next RPC call. After that, the error is reset to nil.
|
||||
func (m *mockConnectorForPoller) setSingleError(err error) {
|
||||
m.mutex.Lock()
|
||||
m.err = err
|
||||
m.persistentError = false
|
||||
m.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) NetworkName() string {
|
||||
return "mockConnectorForPoller"
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) ContractAddress() ethCommon.Address {
|
||||
return e.address
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) GetCurrentGuardianSetIndex(ctx context.Context) (uint32, error) {
|
||||
return 0, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) GetGuardianSet(ctx context.Context, index uint32) (ethAbi.StructsGuardianSet, error) {
|
||||
return ethAbi.StructsGuardianSet{}, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) WatchLogMessagePublished(ctx context.Context, sink chan<- *ethAbi.AbiLogMessagePublished) (ethEvent.Subscription, error) {
|
||||
var s ethEvent.Subscription
|
||||
return s, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) TransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*ethTypes.Receipt, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) TimeOfBlockByHash(ctx context.Context, hash ethCommon.Hash) (uint64, error) {
|
||||
return 0, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) ParseLogMessagePublished(log ethTypes.Log) (*ethAbi.AbiLogMessagePublished, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) SubscribeForBlocks(ctx context.Context, sink chan<- *NewBlock) (ethereum.Subscription, error) {
|
||||
var s ethEvent.Subscription
|
||||
return s, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) RawCallContext(ctx context.Context, result interface{}, method string, args ...interface{}) (err error) {
|
||||
if method != "eth_getBlockByNumber" {
|
||||
panic("method not implemented by mockConnectorForPoller")
|
||||
}
|
||||
|
||||
e.mutex.Lock()
|
||||
// If they set the error, return that immediately.
|
||||
if e.err != nil {
|
||||
err = e.err
|
||||
if !e.persistentError {
|
||||
e.err = nil
|
||||
}
|
||||
} else {
|
||||
str := fmt.Sprintf(`{"author":"0x24c275f0719fdaec6356c4eb9f39ecb9c4d37ce1","baseFeePerGas":"0x3b9aca00","difficulty":"0x0","extraData":"0x","gasLimit":"0xe4e1c0","gasUsed":"0x0","hash":"0xfc8b62a31110121c57cfcccfaf2b147cc2c13b6d01bde4737846cefd29f045cf","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x24c275f0719fdaec6356c4eb9f39ecb9c4d37ce1","nonce":"0x0000000000000000","number":"0x%x","parentHash":"0x09d6d33a658b712f41db7fb9f775f94911ae0132123116aa4f8cf3da9f774e89","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x201","stateRoot":"0x0409ed10e03fd49424ae1489c6fbc6ff1897f45d0e214655ebdb8df94eedc3c0","timestamp":"0x6373ec24","totalDifficulty":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}`, e.blockNumber)
|
||||
err = json.Unmarshal([]byte(str), &result)
|
||||
}
|
||||
e.mutex.Unlock()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) setBlockNumber(blockNumber uint64) {
|
||||
e.mutex.Lock()
|
||||
e.blockNumber = blockNumber
|
||||
e.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) expectedHash() ethCommon.Hash {
|
||||
return ethCommon.HexToHash("0xfc8b62a31110121c57cfcccfaf2b147cc2c13b6d01bde4737846cefd29f045cf")
|
||||
}
|
||||
|
||||
func (e *mockConnectorForPoller) Client() *ethClient.Client {
|
||||
return e.client
|
||||
}
|
||||
|
||||
type mockFinalizerForPoller struct {
|
||||
mutex sync.Mutex
|
||||
finalized bool
|
||||
}
|
||||
|
||||
func newMockFinalizerForPoller(initialState bool) *mockFinalizerForPoller {
|
||||
return &mockFinalizerForPoller{finalized: initialState}
|
||||
}
|
||||
|
||||
func (f *mockFinalizerForPoller) setFinalized(finalized bool) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
f.finalized = finalized
|
||||
}
|
||||
|
||||
func (f *mockFinalizerForPoller) IsBlockFinalized(ctx context.Context, block *NewBlock) (bool, error) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
return f.finalized, nil
|
||||
}
|
||||
|
||||
// TestBlockPoller is one big, ugly test because of all the set up required.
|
||||
func TestBlockPoller(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := mockConnectorForPoller{}
|
||||
|
||||
finalizer := newMockFinalizerForPoller(true) // Start by assuming blocks are finalized.
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
poller := &BlockPollConnector{
|
||||
Connector: &baseConnector,
|
||||
Delay: 1 * time.Millisecond,
|
||||
useFinalized: false,
|
||||
finalizer: finalizer,
|
||||
}
|
||||
|
||||
// Set the starting block.
|
||||
baseConnector.setBlockNumber(0x309a0c)
|
||||
|
||||
// The go routines will post results here.
|
||||
var mutex sync.Mutex
|
||||
var block *NewBlock
|
||||
var err error
|
||||
var pollerStatus int
|
||||
|
||||
// Start the poller running.
|
||||
go func() {
|
||||
mutex.Lock()
|
||||
pollerStatus = 1
|
||||
mutex.Unlock()
|
||||
err := poller.run(ctx, logger)
|
||||
require.NoError(t, err)
|
||||
mutex.Lock()
|
||||
pollerStatus = 2
|
||||
mutex.Unlock()
|
||||
}()
|
||||
|
||||
// Subscribe for events to be processed by our go routine.
|
||||
headSink := make(chan *NewBlock, 2)
|
||||
headerSubscription, suberr := poller.SubscribeForBlocks(ctx, headSink)
|
||||
require.NoError(t, suberr)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case thisErr := <-headerSubscription.Err():
|
||||
mutex.Lock()
|
||||
err = thisErr
|
||||
mutex.Unlock()
|
||||
case thisBlock := <-headSink:
|
||||
require.NotNil(t, thisBlock)
|
||||
mutex.Lock()
|
||||
block = thisBlock
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// First sleep a bit and make sure there were no start up errors.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, block)
|
||||
mutex.Unlock()
|
||||
|
||||
// Post the first new block and verify we get it.
|
||||
baseConnector.setBlockNumber(0x309a0d)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block)
|
||||
assert.Equal(t, uint64(0x309a0d), block.Number.Uint64())
|
||||
assert.Equal(t, baseConnector.expectedHash(), block.Hash)
|
||||
block = nil
|
||||
mutex.Unlock()
|
||||
|
||||
// Sleep some more and verify we don't see any more blocks, since we haven't posted a new one.
|
||||
baseConnector.setBlockNumber(0x309a0d)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, block)
|
||||
mutex.Unlock()
|
||||
|
||||
// Post the next block and verify we get it.
|
||||
baseConnector.setBlockNumber(0x309a0e)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block)
|
||||
assert.Equal(t, uint64(0x309a0e), block.Number.Uint64())
|
||||
assert.Equal(t, baseConnector.expectedHash(), block.Hash)
|
||||
block = nil
|
||||
mutex.Unlock()
|
||||
|
||||
// Post the next block but mark it as not finalized, so we shouldn't see it yet.
|
||||
mutex.Lock()
|
||||
finalizer.setFinalized(false)
|
||||
baseConnector.setBlockNumber(0x309a0f)
|
||||
mutex.Unlock()
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, block)
|
||||
mutex.Unlock()
|
||||
|
||||
// Once it goes finalized we should see it.
|
||||
mutex.Lock()
|
||||
finalizer.setFinalized(true)
|
||||
mutex.Unlock()
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block)
|
||||
assert.Equal(t, uint64(0x309a0f), block.Number.Uint64())
|
||||
assert.Equal(t, baseConnector.expectedHash(), block.Hash)
|
||||
block = nil
|
||||
mutex.Unlock()
|
||||
|
||||
// An RPC error should be returned to us.
|
||||
err = nil
|
||||
baseConnector.setError(fmt.Errorf("RPC failed"))
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, block)
|
||||
baseConnector.setError(nil)
|
||||
err = nil
|
||||
mutex.Unlock()
|
||||
|
||||
// Post the next block and verify we get it (so we survived the RPC error).
|
||||
baseConnector.setBlockNumber(0x309a10)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block)
|
||||
assert.Equal(t, uint64(0x309a10), block.Number.Uint64())
|
||||
assert.Equal(t, baseConnector.expectedHash(), block.Hash)
|
||||
block = nil
|
||||
mutex.Unlock()
|
||||
|
||||
// Post an old block and we should not hear about it.
|
||||
baseConnector.setBlockNumber(0x309a0c)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, block)
|
||||
mutex.Unlock()
|
||||
|
||||
// But we should keep going when we get a new one.
|
||||
baseConnector.setBlockNumber(0x309a11)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block)
|
||||
assert.Equal(t, uint64(0x309a11), block.Number.Uint64())
|
||||
assert.Equal(t, baseConnector.expectedHash(), block.Hash)
|
||||
block = nil
|
||||
mutex.Unlock()
|
||||
|
||||
// If there's a gap in the blocks, we should keep going.
|
||||
baseConnector.setBlockNumber(0x309a13)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block)
|
||||
assert.Equal(t, uint64(0x309a13), block.Number.Uint64())
|
||||
assert.Equal(t, baseConnector.expectedHash(), block.Hash)
|
||||
block = nil
|
||||
mutex.Unlock()
|
||||
|
||||
// Should retry on a transient error and be able to continue.
|
||||
baseConnector.setSingleError(fmt.Errorf("RPC failed"))
|
||||
baseConnector.setBlockNumber(0x309a14)
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
mutex.Lock()
|
||||
require.Equal(t, 1, pollerStatus)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, block)
|
||||
assert.Equal(t, uint64(0x309a14), block.Number.Uint64())
|
||||
assert.Equal(t, baseConnector.expectedHash(), block.Hash)
|
||||
block = nil
|
||||
mutex.Unlock()
|
||||
}
|
|
@ -7,8 +7,6 @@ import (
|
|||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/interfaces"
|
||||
|
||||
ethClient "github.com/ethereum/go-ethereum/ethclient"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -17,20 +15,22 @@ import (
|
|||
|
||||
type ArbitrumFinalizer struct {
|
||||
logger *zap.Logger
|
||||
connector connectors.Connector
|
||||
l1Finalizer interfaces.L1Finalizer
|
||||
}
|
||||
|
||||
func NewArbitrumFinalizer(logger *zap.Logger, connector connectors.Connector, client *ethClient.Client, l1Finalizer interfaces.L1Finalizer) *ArbitrumFinalizer {
|
||||
func NewArbitrumFinalizer(logger *zap.Logger, l1Finalizer interfaces.L1Finalizer) *ArbitrumFinalizer {
|
||||
return &ArbitrumFinalizer{
|
||||
logger: logger,
|
||||
connector: connector,
|
||||
l1Finalizer: l1Finalizer,
|
||||
}
|
||||
}
|
||||
|
||||
// IsBlockFinalized compares the number of the L1 block containing the Arbitrum block with the latest finalized block on Ethereum.
|
||||
func (a *ArbitrumFinalizer) IsBlockFinalized(ctx context.Context, block *connectors.NewBlock) (bool, error) {
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("block is nil")
|
||||
}
|
||||
|
||||
if block.L1BlockNumber == nil {
|
||||
return false, fmt.Errorf("l1 block number is nil")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
package finalizers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// mockL1Finalizer implements the L1Finalizer interface for testing purposes.
|
||||
type mockL1Finalizer struct {
|
||||
LatestFinalizedBlockNumber uint64
|
||||
}
|
||||
|
||||
func (m *mockL1Finalizer) GetLatestFinalizedBlockNumber() uint64 {
|
||||
return m.LatestFinalizedBlockNumber
|
||||
}
|
||||
|
||||
func TestArbitrumErrorReturnedIfBlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, nil)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArbitrumErrorReturnedIfL1BlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: nil,
|
||||
}
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestArbitrumNotFinalizedIfNoFinalizedL1BlockYet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: big.NewInt(225),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
|
||||
func TestArbitrumNotFinalizedWhenFinalizedL1IsLessThanTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 225}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(127),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: big.NewInt(226),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
|
||||
func TestArbitrumIsFinalizedWhenFinalizedL1IsEqualsTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 225}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: big.NewInt(225),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
||||
|
||||
func TestArbitrumIsFinalizedWhenFinalizedL1IsGreaterThanTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 227}
|
||||
|
||||
finalizer := NewArbitrumFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: big.NewInt(225),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
|
@ -2,6 +2,7 @@ package finalizers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
|
||||
|
@ -25,6 +26,10 @@ func NewMoonbeamFinalizer(logger *zap.Logger, connector connectors.Connector) *M
|
|||
}
|
||||
|
||||
func (m *MoonbeamFinalizer) IsBlockFinalized(ctx context.Context, block *connectors.NewBlock) (bool, error) {
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("block is nil")
|
||||
}
|
||||
|
||||
var finalized bool
|
||||
err := m.connector.RawCallContext(ctx, &finalized, "moon_isBlockFinalized", block.Hash.Hex())
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package finalizers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
|
||||
ethAbi "github.com/certusone/wormhole/node/pkg/watchers/evm/connectors/ethabi"
|
||||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
ethTypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethEvent "github.com/ethereum/go-ethereum/event"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type moonbeamMockConnector struct {
|
||||
isFinalized string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) RawCallContext(ctx context.Context, result interface{}, method string, args ...interface{}) (err error) {
|
||||
if method != "moon_isBlockFinalized" {
|
||||
panic("method not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
err = json.Unmarshal([]byte(e.isFinalized), &result)
|
||||
return
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) NetworkName() string {
|
||||
return "moonbeamMockConnector"
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) ContractAddress() ethCommon.Address {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) GetCurrentGuardianSetIndex(ctx context.Context) (uint32, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) GetGuardianSet(ctx context.Context, index uint32) (ethAbi.StructsGuardianSet, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) WatchLogMessagePublished(ctx context.Context, sink chan<- *ethAbi.AbiLogMessagePublished) (ethEvent.Subscription, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) TransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*ethTypes.Receipt, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) TimeOfBlockByHash(ctx context.Context, hash ethCommon.Hash) (uint64, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) ParseLogMessagePublished(log ethTypes.Log) (*ethAbi.AbiLogMessagePublished, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func (e *moonbeamMockConnector) SubscribeForBlocks(ctx context.Context, sink chan<- *connectors.NewBlock) (ethereum.Subscription, error) {
|
||||
panic("not implemented by moonbeamMockConnector")
|
||||
}
|
||||
|
||||
func TestMoonbeamErrorReturnedIfBlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := moonbeamMockConnector{isFinalized: "true", err: nil}
|
||||
|
||||
finalizer := NewMoonbeamFinalizer(logger, &baseConnector)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, nil)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestMoonbeamBlockNotFinalized(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := moonbeamMockConnector{isFinalized: "false", err: nil}
|
||||
|
||||
finalizer := NewMoonbeamFinalizer(logger, &baseConnector)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.HexToHash("0x1076cd8c207f31e1638b37bb358c458f216f5451f06e2ccb4eb9db66ad669f30"),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
func TestMoonbeamBlockIsFinalized(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := moonbeamMockConnector{isFinalized: "true", err: nil}
|
||||
|
||||
finalizer := NewMoonbeamFinalizer(logger, &baseConnector)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.HexToHash("0x1076cd8c207f31e1638b37bb358c458f216f5451f06e2ccb4eb9db66ad669f30"),
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
||||
|
||||
func TestMoonbeamRpcError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
baseConnector := moonbeamMockConnector{isFinalized: "true", err: fmt.Errorf("RPC failed")}
|
||||
|
||||
finalizer := NewMoonbeamFinalizer(logger, &baseConnector)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, nil)
|
||||
require.Error(t, err)
|
||||
}
|
|
@ -2,12 +2,11 @@ package finalizers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/interfaces"
|
||||
|
||||
ethClient "github.com/ethereum/go-ethereum/ethclient"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -16,20 +15,22 @@ import (
|
|||
// Neon team on 11/12/2022. Also confirmed that they do not have a websocket interface so we need to poll for log events.
|
||||
type NeonFinalizer struct {
|
||||
logger *zap.Logger
|
||||
connector connectors.Connector
|
||||
l1Finalizer interfaces.L1Finalizer
|
||||
}
|
||||
|
||||
func NewNeonFinalizer(logger *zap.Logger, connector connectors.Connector, client *ethClient.Client, l1Finalizer interfaces.L1Finalizer) *NeonFinalizer {
|
||||
func NewNeonFinalizer(logger *zap.Logger, l1Finalizer interfaces.L1Finalizer) *NeonFinalizer {
|
||||
return &NeonFinalizer{
|
||||
logger: logger,
|
||||
connector: connector,
|
||||
l1Finalizer: l1Finalizer,
|
||||
}
|
||||
}
|
||||
|
||||
// IsBlockFinalized compares the number of the Neon block with the latest finalized block on Solana.
|
||||
func (f *NeonFinalizer) IsBlockFinalized(ctx context.Context, block *connectors.NewBlock) (bool, error) {
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("block is nil")
|
||||
}
|
||||
|
||||
latestL1Block := f.l1Finalizer.GetLatestFinalizedBlockNumber()
|
||||
if latestL1Block == 0 {
|
||||
// This happens on start up.
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package finalizers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestNeonErrorReturnedIfBlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
|
||||
finalizer := NewNeonFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, nil)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestNeonNotFinalizedIfNoFinalizedL1BlockYet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{}
|
||||
|
||||
finalizer := NewNeonFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: nil,
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
|
||||
func TestNeonNotFinalizedWhenL1IsLessThanL2(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
|
||||
finalizer := NewNeonFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(127),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: nil,
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
|
||||
func TestNeonIsFinalizedWhenL1EqualsL2(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
|
||||
finalizer := NewNeonFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: nil,
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
||||
|
||||
func TestNeonIsFinalizedWhenL1GreaterThanL2(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 127}
|
||||
|
||||
finalizer := NewNeonFinalizer(logger, &l1Finalizer)
|
||||
assert.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
L1BlockNumber: nil,
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
|
@ -32,7 +32,6 @@ import (
|
|||
|
||||
type OptimismFinalizer struct {
|
||||
logger *zap.Logger
|
||||
connector connectors.Connector
|
||||
l1Finalizer interfaces.L1Finalizer
|
||||
latestFinalizedL2Block *big.Int
|
||||
|
||||
|
@ -44,13 +43,17 @@ type OptimismFinalizer struct {
|
|||
ctcClient *ethClient.Client
|
||||
|
||||
// This is used to grab the rollup information from the ctc contract
|
||||
ctcCaller *ctcAbi.OptimismCtcAbiCaller
|
||||
ctcCaller ctcCallerIntf
|
||||
}
|
||||
|
||||
type ctcCallerIntf interface {
|
||||
GetTotalElements(opts *ethBind.CallOpts) (*big.Int, error)
|
||||
GetLastBlockNumber(opts *ethBind.CallOpts) (*big.Int, error)
|
||||
}
|
||||
|
||||
func NewOptimismFinalizer(
|
||||
ctx context.Context,
|
||||
logger *zap.Logger,
|
||||
connector connectors.Connector,
|
||||
l1Finalizer interfaces.L1Finalizer,
|
||||
ctcChainUrl string,
|
||||
ctcChainAddress string,
|
||||
|
@ -72,7 +75,6 @@ func NewOptimismFinalizer(
|
|||
|
||||
finalizer := &OptimismFinalizer{
|
||||
logger: logger,
|
||||
connector: connector,
|
||||
l1Finalizer: l1Finalizer,
|
||||
latestFinalizedL2Block: big.NewInt(0),
|
||||
finalizerMapping: make([]RollupInfo, 0),
|
||||
|
@ -113,6 +115,10 @@ func (f *OptimismFinalizer) GetRollupInfo(ctx context.Context) (RollupInfo, erro
|
|||
}
|
||||
|
||||
func (f *OptimismFinalizer) IsBlockFinalized(ctx context.Context, block *connectors.NewBlock) (bool, error) {
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("block is nil")
|
||||
}
|
||||
|
||||
finalizedL1Block := f.l1Finalizer.GetLatestFinalizedBlockNumber() // Uint64
|
||||
if finalizedL1Block == 0 {
|
||||
// This happens on start up.
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
package finalizers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/evm/connectors"
|
||||
"github.com/certusone/wormhole/node/pkg/watchers/interfaces"
|
||||
|
||||
ethBind "github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
mockCtcCaller struct {
|
||||
mutex sync.Mutex
|
||||
totalElements []*big.Int
|
||||
lastBlockNumbers []*big.Int
|
||||
totalElementsErr error
|
||||
lastBlockNumbersErr error
|
||||
}
|
||||
)
|
||||
|
||||
// SetTotalElements takes an array of big int pointers that represent the L2 block numbers to be returned by GetTotalElements()
|
||||
func (m *mockCtcCaller) SetTotalElements(totalElements []*big.Int) {
|
||||
m.mutex.Lock()
|
||||
m.totalElements = totalElements
|
||||
m.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetLastBlockNumber takes an array of big int pointers that represent the L1 block numbers to be returned by GetLastBlockNumber()
|
||||
func (m *mockCtcCaller) SetLastBlockNumbers(lastBlockNumbers []*big.Int) {
|
||||
m.mutex.Lock()
|
||||
m.lastBlockNumbers = lastBlockNumbers
|
||||
m.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetTotalElementsError takes an error (or nil) which will be returned on the next call to GetTotalElements. The error will persist until cleared.
|
||||
func (m *mockCtcCaller) SetTotalElementsError(err error) {
|
||||
m.mutex.Lock()
|
||||
m.totalElementsErr = err
|
||||
m.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetLastBlockNumber takes an error (or nil) which will be returned on the next call to GetLastBlockNumber. The error will persist until cleared.
|
||||
func (m *mockCtcCaller) SetLastBlockNumberError(err error) {
|
||||
m.mutex.Lock()
|
||||
m.lastBlockNumbersErr = err
|
||||
m.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (m *mockCtcCaller) GetTotalElements(opts *ethBind.CallOpts) (result *big.Int, err error) {
|
||||
m.totalElements, result, err = m.getResult(m.totalElements, m.totalElementsErr)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *mockCtcCaller) GetLastBlockNumber(opts *ethBind.CallOpts) (result *big.Int, err error) {
|
||||
m.lastBlockNumbers, result, err = m.getResult(m.lastBlockNumbers, m.lastBlockNumbersErr)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *mockCtcCaller) getResult(resultsIn []*big.Int, errIn error) (resultsOut []*big.Int, result *big.Int, err error) {
|
||||
for {
|
||||
m.mutex.Lock()
|
||||
// If they set the error, return that immediately.
|
||||
if errIn != nil {
|
||||
err = errIn
|
||||
break
|
||||
}
|
||||
|
||||
// If there are pending results, return the first one.
|
||||
if len(resultsIn) != 0 {
|
||||
result = resultsIn[0]
|
||||
resultsOut = resultsIn[1:]
|
||||
break
|
||||
}
|
||||
|
||||
// If we don't have any results, sleep and try again.
|
||||
m.mutex.Unlock()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
m.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func NewOptimismFinalizerForTest(
|
||||
ctx context.Context,
|
||||
logger *zap.Logger,
|
||||
l1Finalizer interfaces.L1Finalizer,
|
||||
ctcCaller ctcCallerIntf,
|
||||
) *OptimismFinalizer {
|
||||
finalizer := &OptimismFinalizer{
|
||||
logger: logger,
|
||||
l1Finalizer: l1Finalizer,
|
||||
latestFinalizedL2Block: big.NewInt(0),
|
||||
finalizerMapping: make([]RollupInfo, 0),
|
||||
ctcCaller: ctcCaller,
|
||||
}
|
||||
|
||||
return finalizer
|
||||
}
|
||||
|
||||
func TestOptimismErrorReturnedIfBlockIsNil(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
ctcCaller := &mockCtcCaller{}
|
||||
|
||||
finalizer := NewOptimismFinalizerForTest(ctx, logger, &l1Finalizer, ctcCaller)
|
||||
require.NotNil(t, finalizer)
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, nil)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestOptimismNotFinalizedIfNoFinalizedL1BlockYet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{}
|
||||
ctcCaller := mockCtcCaller{}
|
||||
|
||||
finalizer := NewOptimismFinalizerForTest(ctx, logger, &l1Finalizer, &ctcCaller)
|
||||
require.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
|
||||
func TestOptimismNotFinalizedWhenFinalizedL1IsLessThanTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 7954401}
|
||||
ctcCaller := mockCtcCaller{}
|
||||
|
||||
finalizer := NewOptimismFinalizerForTest(ctx, logger, &l1Finalizer, &ctcCaller)
|
||||
require.NotNil(t, finalizer)
|
||||
|
||||
ctcCaller.SetLastBlockNumbers([]*big.Int{big.NewInt(7954402)})
|
||||
ctcCaller.SetTotalElements([]*big.Int{big.NewInt(127)})
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(127),
|
||||
Hash: ethCommon.Hash{},
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, false, finalized)
|
||||
}
|
||||
|
||||
func TestOptimismNotFinalizedWhenFinalizedL1IsEqualsTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 7954402}
|
||||
ctcCaller := mockCtcCaller{}
|
||||
|
||||
finalizer := NewOptimismFinalizerForTest(ctx, logger, &l1Finalizer, &ctcCaller)
|
||||
require.NotNil(t, finalizer)
|
||||
|
||||
ctcCaller.SetLastBlockNumbers([]*big.Int{big.NewInt(7954402)})
|
||||
ctcCaller.SetTotalElements([]*big.Int{big.NewInt(125)})
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
}
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
||||
|
||||
func TestOptimismIsFinalizedWhenFinalizedL1IsGreaterThanTargetL1(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 7954403}
|
||||
ctcCaller := mockCtcCaller{}
|
||||
|
||||
finalizer := NewOptimismFinalizerForTest(ctx, logger, &l1Finalizer, &ctcCaller)
|
||||
require.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
}
|
||||
|
||||
ctcCaller.SetLastBlockNumbers([]*big.Int{big.NewInt(7954402)})
|
||||
ctcCaller.SetTotalElements([]*big.Int{big.NewInt(125)})
|
||||
|
||||
finalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, true, finalized)
|
||||
}
|
||||
|
||||
func TestOptimismL2BlockNumberMustNotGoBackwards(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 7954400}
|
||||
ctcCaller := mockCtcCaller{}
|
||||
|
||||
finalizer := NewOptimismFinalizerForTest(ctx, logger, &l1Finalizer, &ctcCaller)
|
||||
require.NotNil(t, finalizer)
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
}
|
||||
|
||||
ctcCaller.SetLastBlockNumbers([]*big.Int{big.NewInt(7954402), big.NewInt(7954403)})
|
||||
ctcCaller.SetTotalElements([]*big.Int{big.NewInt(124), big.NewInt(123)})
|
||||
|
||||
isFinalized, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, false, isFinalized)
|
||||
|
||||
_, err = finalizer.IsBlockFinalized(ctx, block)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestOptimismGetTotalElementsRpcError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
ctcCaller := mockCtcCaller{}
|
||||
|
||||
finalizer := NewOptimismFinalizerForTest(ctx, logger, &l1Finalizer, &ctcCaller)
|
||||
require.NotNil(t, finalizer)
|
||||
|
||||
ctcCaller.SetTotalElementsError(fmt.Errorf("RPC failed"))
|
||||
ctcCaller.SetLastBlockNumbers([]*big.Int{big.NewInt(7954402)})
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
}
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestOptimismGetLastBlockNumberRpcError(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
logger := zap.NewNop()
|
||||
l1Finalizer := mockL1Finalizer{LatestFinalizedBlockNumber: 125}
|
||||
ctcCaller := mockCtcCaller{}
|
||||
|
||||
finalizer := NewOptimismFinalizerForTest(ctx, logger, &l1Finalizer, &ctcCaller)
|
||||
require.NotNil(t, finalizer)
|
||||
|
||||
ctcCaller.SetLastBlockNumberError(fmt.Errorf("RPC failed"))
|
||||
ctcCaller.SetTotalElements([]*big.Int{big.NewInt(125)})
|
||||
|
||||
block := &connectors.NewBlock{
|
||||
Number: big.NewInt(125),
|
||||
Hash: ethCommon.Hash{},
|
||||
}
|
||||
|
||||
_, err := finalizer.IsBlockFinalized(ctx, block)
|
||||
require.Error(t, err)
|
||||
}
|
|
@ -242,7 +242,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
finalizer := finalizers.NewNeonFinalizer(logger, baseConnector, baseConnector.Client(), w.l1Finalizer)
|
||||
finalizer := finalizers.NewNeonFinalizer(logger, w.l1Finalizer)
|
||||
pollConnector, err := connectors.NewBlockPollConnector(ctx, baseConnector, finalizer, 250*time.Millisecond, false, false)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
|
@ -265,7 +265,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
finalizer := finalizers.NewArbitrumFinalizer(logger, baseConnector, baseConnector.Client(), w.l1Finalizer)
|
||||
finalizer := finalizers.NewArbitrumFinalizer(logger, w.l1Finalizer)
|
||||
pollConnector, err := connectors.NewBlockPollConnector(ctx, baseConnector, finalizer, 250*time.Millisecond, false, false)
|
||||
if err != nil {
|
||||
ethConnectionErrors.WithLabelValues(w.networkName, "dial_error").Inc()
|
||||
|
@ -288,7 +288,7 @@ func (w *Watcher) Run(ctx context.Context) error {
|
|||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("dialing eth client failed: %w", err)
|
||||
}
|
||||
finalizer, err := finalizers.NewOptimismFinalizer(timeout, logger, baseConnector, w.l1Finalizer, w.rootChainRpc, w.rootChainContract)
|
||||
finalizer, err := finalizers.NewOptimismFinalizer(timeout, logger, w.l1Finalizer, w.rootChainRpc, w.rootChainContract)
|
||||
if err != nil {
|
||||
p2p.DefaultRegistry.AddErrorCount(w.chainID, 1)
|
||||
return fmt.Errorf("creating optimism finalizer failed: %w", err)
|
||||
|
|
Loading…
Reference in New Issue