mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-20 19:37:55 -05:00
438 lines
15 KiB
Go
438 lines
15 KiB
Go
package watcher
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"errors"
|
|
"math/big"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/agiledragon/gomonkey/v2"
|
|
"github.com/scroll-tech/go-ethereum/accounts/abi"
|
|
"github.com/scroll-tech/go-ethereum/accounts/abi/bind"
|
|
"github.com/scroll-tech/go-ethereum/common"
|
|
geth_types "github.com/scroll-tech/go-ethereum/core/types"
|
|
"github.com/scroll-tech/go-ethereum/ethclient"
|
|
"github.com/scroll-tech/go-ethereum/rpc"
|
|
"github.com/smartystreets/goconvey/convey"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"scroll-tech/common/types"
|
|
|
|
bridge_abi "scroll-tech/bridge/abi"
|
|
"scroll-tech/bridge/mock_bridge"
|
|
"scroll-tech/bridge/sender"
|
|
"scroll-tech/bridge/utils"
|
|
|
|
cutils "scroll-tech/common/utils"
|
|
|
|
"scroll-tech/database"
|
|
"scroll-tech/database/migrate"
|
|
)
|
|
|
|
func setupL2Watcher(t *testing.T) *L2WatcherClient {
|
|
db, err := database.NewOrmFactory(cfg.DBConfig)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, migrate.ResetDB(db.GetDB().DB))
|
|
defer db.Close()
|
|
|
|
l2cfg := cfg.L2Config
|
|
watcher := NewL2WatcherClient(context.Background(), l2Cli, l2cfg.Confirmations, l2cfg.L2MessengerAddress, l2cfg.L2MessageQueueAddress, l2cfg.WithdrawTrieRootSlot, db)
|
|
return watcher
|
|
}
|
|
|
|
func testCreateNewWatcherAndStop(t *testing.T) {
|
|
// Create db handler and reset db.
|
|
l2db, err := database.NewOrmFactory(cfg.DBConfig)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, migrate.ResetDB(l2db.GetDB().DB))
|
|
ctx := context.Background()
|
|
subCtx, cancel := context.WithCancel(ctx)
|
|
defer func() {
|
|
cancel()
|
|
l2db.Close()
|
|
}()
|
|
|
|
l2cfg := cfg.L2Config
|
|
wc := NewL2WatcherClient(context.Background(), l2Cli, l2cfg.Confirmations, l2cfg.L2MessengerAddress, l2cfg.L2MessageQueueAddress, l2cfg.WithdrawTrieRootSlot, l2db)
|
|
loopToFetchEvent(subCtx, wc)
|
|
|
|
l1cfg := cfg.L1Config
|
|
l1cfg.RelayerConfig.SenderConfig.Confirmations = rpc.LatestBlockNumber
|
|
newSender, err := sender.NewSender(context.Background(), l1cfg.RelayerConfig.SenderConfig, l1cfg.RelayerConfig.MessageSenderPrivateKeys)
|
|
assert.NoError(t, err)
|
|
|
|
// Create several transactions and commit to block
|
|
numTransactions := 3
|
|
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
for i := 0; i < numTransactions; i++ {
|
|
_, err = newSender.SendTransaction(strconv.Itoa(1000+i), &toAddress, big.NewInt(1000000000), nil, 0)
|
|
assert.NoError(t, err)
|
|
<-newSender.ConfirmChan()
|
|
}
|
|
|
|
blockNum, err := l2Cli.BlockNumber(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, blockNum, uint64(numTransactions))
|
|
}
|
|
|
|
func testMonitorBridgeContract(t *testing.T) {
|
|
// Create db handler and reset db.
|
|
db, err := database.NewOrmFactory(cfg.DBConfig)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, migrate.ResetDB(db.GetDB().DB))
|
|
ctx := context.Background()
|
|
subCtx, cancel := context.WithCancel(ctx)
|
|
|
|
defer func() {
|
|
cancel()
|
|
db.Close()
|
|
}()
|
|
|
|
l2cfg := cfg.L2Config
|
|
wc := NewL2WatcherClient(context.Background(), l2Cli, l2cfg.Confirmations, l2cfg.L2MessengerAddress, l2cfg.L2MessageQueueAddress, l2cfg.WithdrawTrieRootSlot, db)
|
|
loopToFetchEvent(subCtx, wc)
|
|
|
|
previousHeight, err := l2Cli.BlockNumber(context.Background())
|
|
assert.NoError(t, err)
|
|
|
|
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
|
|
|
// deploy mock bridge
|
|
_, tx, instance, err := mock_bridge.DeployMockBridgeL2(auth, l2Cli)
|
|
assert.NoError(t, err)
|
|
address, err := bind.WaitDeployed(context.Background(), l2Cli, tx)
|
|
assert.NoError(t, err)
|
|
|
|
rc := prepareWatcherClient(l2Cli, db, address)
|
|
loopToFetchEvent(subCtx, rc)
|
|
// Call mock_bridge instance sendMessage to trigger emit events
|
|
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
message := []byte("testbridgecontract")
|
|
fee := big.NewInt(0)
|
|
gasLimit := big.NewInt(1)
|
|
|
|
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
|
assert.NoError(t, err)
|
|
receipt, err := bind.WaitMined(context.Background(), l2Cli, tx)
|
|
if receipt.Status != geth_types.ReceiptStatusSuccessful || err != nil {
|
|
t.Fatalf("Call failed")
|
|
}
|
|
|
|
// extra block mined
|
|
toAddress = common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
message = []byte("testbridgecontract")
|
|
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
|
assert.NoError(t, err)
|
|
receipt, err = bind.WaitMined(context.Background(), l2Cli, tx)
|
|
if receipt.Status != geth_types.ReceiptStatusSuccessful || err != nil {
|
|
t.Fatalf("Call failed")
|
|
}
|
|
|
|
// check if we successfully stored events
|
|
assert.True(t, cutils.TryTimes(10, func() bool {
|
|
height, err := db.GetLayer2LatestWatchedHeight()
|
|
return err == nil && height > int64(previousHeight)
|
|
}))
|
|
|
|
// check l1 messages.
|
|
assert.True(t, cutils.TryTimes(10, func() bool {
|
|
msgs, err := db.GetL2Messages(map[string]interface{}{"status": types.MsgPending})
|
|
return err == nil && len(msgs) == 2
|
|
}))
|
|
}
|
|
|
|
func testFetchMultipleSentMessageInOneBlock(t *testing.T) {
|
|
// Create db handler and reset db.
|
|
db, err := database.NewOrmFactory(cfg.DBConfig)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, migrate.ResetDB(db.GetDB().DB))
|
|
ctx := context.Background()
|
|
subCtx, cancel := context.WithCancel(ctx)
|
|
|
|
defer func() {
|
|
cancel()
|
|
db.Close()
|
|
}()
|
|
|
|
previousHeight, err := l2Cli.BlockNumber(context.Background()) // shallow the global previousHeight
|
|
assert.NoError(t, err)
|
|
|
|
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
|
|
|
_, trx, instance, err := mock_bridge.DeployMockBridgeL2(auth, l2Cli)
|
|
assert.NoError(t, err)
|
|
address, err := bind.WaitDeployed(context.Background(), l2Cli, trx)
|
|
assert.NoError(t, err)
|
|
|
|
wc := prepareWatcherClient(l2Cli, db, address)
|
|
loopToFetchEvent(subCtx, wc)
|
|
|
|
// Call mock_bridge instance sendMessage to trigger emit events multiple times
|
|
numTransactions := 4
|
|
var tx *geth_types.Transaction
|
|
|
|
for i := 0; i < numTransactions; i++ {
|
|
addr := common.HexToAddress("0x1c5a77d9fa7ef466951b2f01f724bca3a5820b63")
|
|
nonce, nounceErr := l2Cli.PendingNonceAt(context.Background(), addr)
|
|
assert.NoError(t, nounceErr)
|
|
auth.Nonce = big.NewInt(int64(nonce))
|
|
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
message := []byte("testbridgecontract")
|
|
fee := big.NewInt(0)
|
|
gasLimit := big.NewInt(1)
|
|
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
receipt, err := bind.WaitMined(context.Background(), l2Cli, tx)
|
|
if receipt.Status != geth_types.ReceiptStatusSuccessful || err != nil {
|
|
t.Fatalf("Call failed")
|
|
}
|
|
|
|
// extra block mined
|
|
addr := common.HexToAddress("0x1c5a77d9fa7ef466951b2f01f724bca3a5820b63")
|
|
nonce, nounceErr := l2Cli.PendingNonceAt(context.Background(), addr)
|
|
assert.NoError(t, nounceErr)
|
|
auth.Nonce = big.NewInt(int64(nonce))
|
|
toAddress := common.HexToAddress("0x4592d8f8d7b001e72cb26a73e4fa1806a51ac79d")
|
|
message := []byte("testbridgecontract")
|
|
fee := big.NewInt(0)
|
|
gasLimit := big.NewInt(1)
|
|
tx, err = instance.SendMessage(auth, toAddress, fee, message, gasLimit)
|
|
assert.NoError(t, err)
|
|
receipt, err = bind.WaitMined(context.Background(), l2Cli, tx)
|
|
if receipt.Status != geth_types.ReceiptStatusSuccessful || err != nil {
|
|
t.Fatalf("Call failed")
|
|
}
|
|
|
|
// check if we successfully stored events
|
|
assert.True(t, cutils.TryTimes(10, func() bool {
|
|
height, err := db.GetLayer2LatestWatchedHeight()
|
|
return err == nil && height > int64(previousHeight)
|
|
}))
|
|
|
|
assert.True(t, cutils.TryTimes(10, func() bool {
|
|
msgs, err := db.GetL2Messages(map[string]interface{}{"status": types.MsgPending})
|
|
return err == nil && len(msgs) == 5
|
|
}))
|
|
}
|
|
|
|
func testFetchRunningMissingBlocks(t *testing.T) {
|
|
// Create db handler and reset db.
|
|
db, err := database.NewOrmFactory(cfg.DBConfig)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, migrate.ResetDB(db.GetDB().DB))
|
|
defer db.Close()
|
|
|
|
auth := prepareAuth(t, l2Cli, cfg.L2Config.RelayerConfig.MessageSenderPrivateKeys[0])
|
|
|
|
// deploy mock bridge
|
|
_, tx, _, err := mock_bridge.DeployMockBridgeL2(auth, l2Cli)
|
|
assert.NoError(t, err)
|
|
address, err := bind.WaitDeployed(context.Background(), l2Cli, tx)
|
|
assert.NoError(t, err)
|
|
|
|
ok := cutils.TryTimes(10, func() bool {
|
|
latestHeight, err := l2Cli.BlockNumber(context.Background())
|
|
if err != nil {
|
|
return false
|
|
}
|
|
wc := prepareWatcherClient(l2Cli, db, address)
|
|
wc.TryFetchRunningMissingBlocks(context.Background(), latestHeight)
|
|
fetchedHeight, err := db.GetL2BlocksLatestHeight()
|
|
return err == nil && uint64(fetchedHeight) == latestHeight
|
|
})
|
|
assert.True(t, ok)
|
|
}
|
|
|
|
func prepareWatcherClient(l2Cli *ethclient.Client, db database.OrmFactory, contractAddr common.Address) *L2WatcherClient {
|
|
confirmations := rpc.LatestBlockNumber
|
|
return NewL2WatcherClient(context.Background(), l2Cli, confirmations, contractAddr, contractAddr, common.Hash{}, db)
|
|
}
|
|
|
|
func prepareAuth(t *testing.T, l2Cli *ethclient.Client, privateKey *ecdsa.PrivateKey) *bind.TransactOpts {
|
|
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(53077))
|
|
assert.NoError(t, err)
|
|
auth.Value = big.NewInt(0) // in wei
|
|
assert.NoError(t, err)
|
|
auth.GasPrice, err = l2Cli.SuggestGasPrice(context.Background())
|
|
assert.NoError(t, err)
|
|
return auth
|
|
}
|
|
|
|
func loopToFetchEvent(subCtx context.Context, watcher *L2WatcherClient) {
|
|
go cutils.Loop(subCtx, 2*time.Second, watcher.FetchContractEvent)
|
|
}
|
|
|
|
func testParseBridgeEventLogsL2SentMessageEventSignature(t *testing.T) {
|
|
watcher := setupL2Watcher(t)
|
|
logs := []geth_types.Log{
|
|
{
|
|
Topics: []common.Hash{
|
|
bridge_abi.L2SentMessageEventSignature,
|
|
},
|
|
BlockNumber: 100,
|
|
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
},
|
|
}
|
|
|
|
convey.Convey("unpack SentMessage log failure", t, func() {
|
|
targetErr := errors.New("UnpackLog SentMessage failure")
|
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log geth_types.Log) error {
|
|
return targetErr
|
|
})
|
|
defer patchGuard.Reset()
|
|
|
|
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
assert.EqualError(t, err, targetErr.Error())
|
|
assert.Empty(t, l2Messages)
|
|
assert.Empty(t, relayedMessages)
|
|
})
|
|
|
|
convey.Convey("L2SentMessageEventSignature success", t, func() {
|
|
tmpSendAddr := common.HexToAddress("0xb4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30")
|
|
tmpTargetAddr := common.HexToAddress("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
|
tmpValue := big.NewInt(1000)
|
|
tmpMessageNonce := big.NewInt(100)
|
|
tmpMessage := []byte("test for L2SentMessageEventSignature")
|
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log geth_types.Log) error {
|
|
tmpOut := out.(*bridge_abi.L2SentMessageEvent)
|
|
tmpOut.Sender = tmpSendAddr
|
|
tmpOut.Value = tmpValue
|
|
tmpOut.Target = tmpTargetAddr
|
|
tmpOut.MessageNonce = tmpMessageNonce
|
|
tmpOut.Message = tmpMessage
|
|
return nil
|
|
})
|
|
defer patchGuard.Reset()
|
|
|
|
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
assert.Error(t, err)
|
|
assert.Empty(t, relayedMessages)
|
|
assert.Empty(t, l2Messages)
|
|
})
|
|
}
|
|
|
|
func testParseBridgeEventLogsL2RelayedMessageEventSignature(t *testing.T) {
|
|
watcher := setupL2Watcher(t)
|
|
logs := []geth_types.Log{
|
|
{
|
|
Topics: []common.Hash{bridge_abi.L2RelayedMessageEventSignature},
|
|
BlockNumber: 100,
|
|
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
},
|
|
}
|
|
|
|
convey.Convey("unpack RelayedMessage log failure", t, func() {
|
|
targetErr := errors.New("UnpackLog RelayedMessage failure")
|
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log geth_types.Log) error {
|
|
return targetErr
|
|
})
|
|
defer patchGuard.Reset()
|
|
|
|
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
assert.EqualError(t, err, targetErr.Error())
|
|
assert.Empty(t, l2Messages)
|
|
assert.Empty(t, relayedMessages)
|
|
})
|
|
|
|
convey.Convey("L2RelayedMessageEventSignature success", t, func() {
|
|
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log geth_types.Log) error {
|
|
tmpOut := out.(*bridge_abi.L2RelayedMessageEvent)
|
|
tmpOut.MessageHash = msgHash
|
|
return nil
|
|
})
|
|
defer patchGuard.Reset()
|
|
|
|
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, l2Messages)
|
|
assert.Len(t, relayedMessages, 1)
|
|
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
|
})
|
|
}
|
|
|
|
func testParseBridgeEventLogsL2FailedRelayedMessageEventSignature(t *testing.T) {
|
|
watcher := setupL2Watcher(t)
|
|
logs := []geth_types.Log{
|
|
{
|
|
Topics: []common.Hash{bridge_abi.L2FailedRelayedMessageEventSignature},
|
|
BlockNumber: 100,
|
|
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
},
|
|
}
|
|
|
|
convey.Convey("unpack FailedRelayedMessage log failure", t, func() {
|
|
targetErr := errors.New("UnpackLog FailedRelayedMessage failure")
|
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log geth_types.Log) error {
|
|
return targetErr
|
|
})
|
|
defer patchGuard.Reset()
|
|
|
|
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
assert.EqualError(t, err, targetErr.Error())
|
|
assert.Empty(t, l2Messages)
|
|
assert.Empty(t, relayedMessages)
|
|
})
|
|
|
|
convey.Convey("L2FailedRelayedMessageEventSignature success", t, func() {
|
|
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log geth_types.Log) error {
|
|
tmpOut := out.(*bridge_abi.L2FailedRelayedMessageEvent)
|
|
tmpOut.MessageHash = msgHash
|
|
return nil
|
|
})
|
|
defer patchGuard.Reset()
|
|
|
|
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, l2Messages)
|
|
assert.Len(t, relayedMessages, 1)
|
|
assert.Equal(t, relayedMessages[0].msgHash, msgHash)
|
|
})
|
|
}
|
|
|
|
func testParseBridgeEventLogsL2AppendMessageEventSignature(t *testing.T) {
|
|
watcher := setupL2Watcher(t)
|
|
logs := []geth_types.Log{
|
|
{
|
|
Topics: []common.Hash{bridge_abi.L2AppendMessageEventSignature},
|
|
BlockNumber: 100,
|
|
TxHash: common.HexToHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"),
|
|
},
|
|
}
|
|
|
|
convey.Convey("unpack AppendMessage log failure", t, func() {
|
|
targetErr := errors.New("UnpackLog AppendMessage failure")
|
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log geth_types.Log) error {
|
|
return targetErr
|
|
})
|
|
defer patchGuard.Reset()
|
|
|
|
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
assert.EqualError(t, err, targetErr.Error())
|
|
assert.Empty(t, l2Messages)
|
|
assert.Empty(t, relayedMessages)
|
|
})
|
|
|
|
convey.Convey("L2AppendMessageEventSignature success", t, func() {
|
|
msgHash := common.HexToHash("0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5")
|
|
patchGuard := gomonkey.ApplyFunc(utils.UnpackLog, func(c *abi.ABI, out interface{}, event string, log geth_types.Log) error {
|
|
tmpOut := out.(*bridge_abi.L2AppendMessageEvent)
|
|
tmpOut.MessageHash = msgHash
|
|
tmpOut.Index = big.NewInt(100)
|
|
return nil
|
|
})
|
|
defer patchGuard.Reset()
|
|
|
|
l2Messages, relayedMessages, err := watcher.parseBridgeEventLogs(logs)
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, l2Messages)
|
|
assert.Empty(t, relayedMessages)
|
|
})
|
|
}
|