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: integrate the aggoracle contracts #244

Merged
merged 21 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5433442
integrate new contracts for the aggoracle (wip)
Stefan-Ethernal Dec 12, 2024
0ef8ba1
feat: integrate the GlobalExitRootManager L2 sovereign chain
Stefan-Ethernal Dec 17, 2024
ddad93b
fix: backport changes from fep-type1 branch
Stefan-Ethernal Dec 17, 2024
029d73c
chore: remove empty test/e2e.go
Stefan-Ethernal Dec 17, 2024
d51548b
test: update the aggoracle e2e test
Stefan-Ethernal Dec 17, 2024
8719da5
fix: logs
Stefan-Ethernal Dec 18, 2024
70a68e1
refactor: rely only on single L1 bridge syncer in the e2e, cleanup lo…
Stefan-Ethernal Dec 18, 2024
85c0566
refactor: simulated backend so that it correctly initializes bridge c…
Stefan-Ethernal Dec 18, 2024
cb1eee9
fix: tests
Stefan-Ethernal Dec 18, 2024
3eb029e
fix: bridge balance logs
Stefan-Ethernal Dec 18, 2024
e3cd98e
add extract rpc error data helper function
Stefan-Ethernal Dec 19, 2024
897cbbc
extract L1SetupResult and L2SetupResult
Stefan-Ethernal Dec 19, 2024
fb51557
some more cleanups
Stefan-Ethernal Dec 19, 2024
6f9cb74
fix: update import paths for l2-sovereign-chain contracts and use the…
Stefan-Ethernal Dec 19, 2024
f7e69de
refactor: rename waitTxToBeSuccessOrFail to waitForTxResult and updat…
Stefan-Ethernal Dec 19, 2024
fca049d
chore: update cdk-contracts-tooling dependency to v0.0.1
Stefan-Ethernal Dec 19, 2024
1a95a1e
fix: remove duplicated recipes in test/makefile
Stefan-Ethernal Dec 20, 2024
1d5a141
test: chaingersender ut
Stefan-Ethernal Dec 20, 2024
de60d6f
test: IsGERInjected unit test
Stefan-Ethernal Dec 20, 2024
b4949a0
remove removal of mock files
Stefan-Ethernal Dec 23, 2024
85bbb5b
rename
Stefan-Ethernal Dec 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agglayer/mock_agglayer_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 64 additions & 44 deletions aggoracle/chaingersender/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@ import (
"math/big"
"time"

"github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/pessimisticglobalexitroot"
cfgTypes "github.com/0xPolygon/cdk/config/types"
"github.com/0xPolygon/cdk-contracts-tooling/contracts/l2-sovereign-chain/globalexitrootmanagerl2sovereignchain"
cdkcommon "github.com/0xPolygon/cdk/common"
cfgtypes "github.com/0xPolygon/cdk/config/types"
"github.com/0xPolygon/cdk/log"
"github.com/0xPolygon/zkevm-ethtx-manager/ethtxmanager"
ethtxtypes "github.com/0xPolygon/zkevm-ethtx-manager/types"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

const insertGERFuncName = "insertGlobalExitRoot"

type EthClienter interface {
ethereum.LogFilterer
ethereum.BlockNumberReader
Expand All @@ -39,97 +43,113 @@ type EthTxManager interface {
) (common.Hash, error)
}

type EVMChainGERSender struct {
logger *log.Logger
gerContract *pessimisticglobalexitroot.Pessimisticglobalexitroot
gerAddr common.Address
client EthClienter
ethTxMan EthTxManager
gasOffset uint64
waitPeriodMonitorTx time.Duration
type L2GERManager interface {
GlobalExitRootMap(opts *bind.CallOpts, ger [common.HashLength]byte) (*big.Int, error)
}

type EVMConfig struct {
GlobalExitRootL2Addr common.Address `mapstructure:"GlobalExitRootL2"`
URLRPCL2 string `mapstructure:"URLRPCL2"`
ChainIDL2 uint64 `mapstructure:"ChainIDL2"`
GasOffset uint64 `mapstructure:"GasOffset"`
WaitPeriodMonitorTx cfgTypes.Duration `mapstructure:"WaitPeriodMonitorTx"`
WaitPeriodMonitorTx cfgtypes.Duration `mapstructure:"WaitPeriodMonitorTx"`
EthTxManager ethtxmanager.Config `mapstructure:"EthTxManager"`
}

type EVMChainGERSender struct {
logger *log.Logger

l2GERManager L2GERManager
l2GERManagerAddr common.Address
l2GERManagerAbi *abi.ABI

ethTxMan EthTxManager
gasOffset uint64
waitPeriodMonitorTx time.Duration
}

func NewEVMChainGERSender(
logger *log.Logger,
l2GlobalExitRoot common.Address,
l2GERManagerAddr common.Address,
l2Client EthClienter,
ethTxMan EthTxManager,
gasOffset uint64,
waitPeriodMonitorTx time.Duration,
) (*EVMChainGERSender, error) {
gerContract, err := pessimisticglobalexitroot.NewPessimisticglobalexitroot(l2GlobalExitRoot, l2Client)
l2GERManager, err := globalexitrootmanagerl2sovereignchain.NewGlobalexitrootmanagerl2sovereignchain(
l2GERManagerAddr, l2Client)
if err != nil {
return nil, err
}

l2GERAbi, err := globalexitrootmanagerl2sovereignchain.Globalexitrootmanagerl2sovereignchainMetaData.GetAbi()
if err != nil {
return nil, err
}

return &EVMChainGERSender{
logger: logger,
gerContract: gerContract,
gerAddr: l2GlobalExitRoot,
client: l2Client,
l2GERManager: l2GERManager,
l2GERManagerAddr: l2GERManagerAddr,
l2GERManagerAbi: l2GERAbi,
ethTxMan: ethTxMan,
gasOffset: gasOffset,
waitPeriodMonitorTx: waitPeriodMonitorTx,
}, nil
}

func (c *EVMChainGERSender) IsGERInjected(ger common.Hash) (bool, error) {
timestamp, err := c.gerContract.GlobalExitRootMap(&bind.CallOpts{Pending: false}, ger)
blockHashBigInt, err := c.l2GERManager.GlobalExitRootMap(&bind.CallOpts{Pending: false}, ger)
if err != nil {
return false, fmt.Errorf("error calling gerContract.GlobalExitRootMap: %w", err)
return false, fmt.Errorf("failed to check if global exit root is injected %s: %w", ger, err)
}

return timestamp.Cmp(common.Big0) != 0, nil
return common.BigToHash(blockHashBigInt) != cdkcommon.ZeroHash, nil
}

func (c *EVMChainGERSender) InjectGER(ctx context.Context, ger common.Hash) error {
ticker := time.NewTicker(c.waitPeriodMonitorTx)
defer ticker.Stop()

gerABI, err := pessimisticglobalexitroot.PessimisticglobalexitrootMetaData.GetAbi()
updateGERTxInput, err := c.l2GERManagerAbi.Pack(insertGERFuncName, ger)
if err != nil {
return err
}

updateGERTxInput, err := gerABI.Pack("updateGlobalExitRoot", ger)
id, err := c.ethTxMan.Add(ctx, &c.l2GERManagerAddr, common.Big0, updateGERTxInput, c.gasOffset, nil)
if err != nil {
return err
}

id, err := c.ethTxMan.Add(ctx, &c.gerAddr, big.NewInt(0), updateGERTxInput, c.gasOffset, nil)
if err != nil {
return err
}
for {
<-ticker.C

c.logger.Debugf("waiting for tx %s to be mined", id.Hex())
res, err := c.ethTxMan.Result(ctx, id)
if err != nil {
c.logger.Error("error calling ethTxMan.Result: ", err)
}

switch res.Status {
case ethtxtypes.MonitoredTxStatusCreated,
ethtxtypes.MonitoredTxStatusSent:
continue
case ethtxtypes.MonitoredTxStatusFailed:
return fmt.Errorf("tx %s failed", res.ID)
case ethtxtypes.MonitoredTxStatusMined,
ethtxtypes.MonitoredTxStatusSafe,
ethtxtypes.MonitoredTxStatusFinalized:
select {
case <-ctx.Done():
c.logger.Infof("context cancelled")
return nil
default:
c.logger.Error("unexpected tx status: ", res.Status)

case <-ticker.C:
c.logger.Debugf("waiting for tx %s to be mined", id.Hex())
res, err := c.ethTxMan.Result(ctx, id)
if err != nil {
c.logger.Errorf("failed to check the transaction %s status: %s", id.Hex(), err)
return err
}

switch res.Status {
case ethtxtypes.MonitoredTxStatusCreated,
ethtxtypes.MonitoredTxStatusSent:
continue
case ethtxtypes.MonitoredTxStatusFailed:
return fmt.Errorf("inject GER tx %s failed", id.Hex())
case ethtxtypes.MonitoredTxStatusMined,
ethtxtypes.MonitoredTxStatusSafe,
ethtxtypes.MonitoredTxStatusFinalized:
c.logger.Debugf("inject GER tx %s was successfully mined at block %d", id.Hex(), res.MinedAtBlockNumber)

return nil
default:
c.logger.Error("unexpected tx status:", res.Status)
}
}
}
}
168 changes: 168 additions & 0 deletions aggoracle/chaingersender/evm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package chaingersender

import (
"context"
"errors"
"math/big"
"strings"
"testing"
"time"

"github.com/0xPolygon/cdk/aggoracle/mocks"
"github.com/0xPolygon/cdk/log"
"github.com/0xPolygon/zkevm-ethtx-manager/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

func TestEVMChainGERSender_InjectGER(t *testing.T) {
insertGERFuncABI := `[{
"inputs": [
{
"internalType": "bytes32",
"name": "_newRoot",
"type": "bytes32"
}
],
"name": "insertGlobalExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}]`
l2GERManagerAddr := common.HexToAddress("0x123")
l2GERManagerAbi, err := abi.JSON(strings.NewReader(insertGERFuncABI))
require.NoError(t, err)

ger := common.HexToHash("0x456")
txID := common.HexToHash("0x789")

tests := []struct {
name string
addReturnTxID common.Hash
addReturnErr error
resultReturn types.MonitoredTxResult
resultReturnErr error
expectedErr string
}{
{
name: "successful injection",
addReturnTxID: txID,
addReturnErr: nil,
resultReturn: types.MonitoredTxResult{Status: types.MonitoredTxStatusMined, MinedAtBlockNumber: big.NewInt(123)},
resultReturnErr: nil,
expectedErr: "",
},
{
name: "injection fails due to transaction failure",
addReturnTxID: txID,
addReturnErr: nil,
resultReturn: types.MonitoredTxResult{Status: types.MonitoredTxStatusFailed},
resultReturnErr: nil,
expectedErr: "inject GER tx",
},
{
name: "injection fails due to Add method error",
addReturnTxID: common.Hash{},
addReturnErr: errors.New("add error"),
resultReturn: types.MonitoredTxResult{},
resultReturnErr: nil,
expectedErr: "add error",
},
{
name: "injection fails due to Result method error",
addReturnTxID: txID,
addReturnErr: nil,
resultReturn: types.MonitoredTxResult{},
resultReturnErr: errors.New("result error"),
expectedErr: "result error",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, cancelFn := context.WithTimeout(context.Background(), time.Millisecond*500)
defer cancelFn()

ethTxMan := new(mocks.EthTxManagerMock)
ethTxMan.
On("Add", ctx, &l2GERManagerAddr, common.Big0, mock.Anything, mock.Anything, mock.Anything).
Return(tt.addReturnTxID, tt.addReturnErr)
ethTxMan.
On("Result", ctx, tt.addReturnTxID).
Return(tt.resultReturn, tt.resultReturnErr)

sender := &EVMChainGERSender{
logger: log.GetDefaultLogger(),
l2GERManagerAddr: l2GERManagerAddr,
l2GERManagerAbi: &l2GERManagerAbi,
ethTxMan: ethTxMan,
waitPeriodMonitorTx: time.Millisecond * 10,
}

err := sender.InjectGER(ctx, ger)
if tt.expectedErr == "" {
require.NoError(t, err)
} else {
require.Error(t, err)
require.Contains(t, err.Error(), tt.expectedErr)
}
})
}
}

func TestEVMChainGERSender_IsGERInjected(t *testing.T) {
tests := []struct {
name string
mockReturn *big.Int
mockError error
expectedResult bool
expectedErrMsg string
}{
{
name: "GER is injected",
mockReturn: big.NewInt(1),
mockError: nil,
expectedResult: true,
expectedErrMsg: "",
},
{
name: "GER is not injected",
mockReturn: big.NewInt(0),
mockError: nil,
expectedResult: false,
expectedErrMsg: "",
},
{
name: "Error checking GER injection",
mockReturn: nil,
mockError: errors.New("some error"),
expectedResult: false,
expectedErrMsg: "failed to check if global exit root is injected",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockL2GERManager := new(mocks.L2GERManagerMock)
mockL2GERManager.On("GlobalExitRootMap", mock.Anything, mock.Anything).
Return(tt.mockReturn, tt.mockError)

evmChainGERSender := &EVMChainGERSender{
l2GERManager: mockL2GERManager,
}

ger := common.HexToHash("0x12345")
result, err := evmChainGERSender.IsGERInjected(ger)
if tt.expectedErrMsg != "" {
require.ErrorContains(t, err, tt.expectedErrMsg)
} else {
require.NoError(t, err)
}
require.Equal(t, tt.expectedResult, result)

mockL2GERManager.AssertExpectations(t)
})
}
}
Loading
Loading