Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaubennassar committed Jul 24, 2024
1 parent dabcf40 commit 2c6db44
Show file tree
Hide file tree
Showing 9 changed files with 410 additions and 33 deletions.
114 changes: 114 additions & 0 deletions aggoracle/chaingersender/evm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package chaingersender

import (
"context"
"fmt"
"math/big"
"time"

"github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/pessimisticglobalexitroot"
"github.com/0xPolygon/cdk/log"
"github.com/0xPolygonHermez/zkevm-ethtx-manager/ethtxmanager"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

var (
waitPeriodMonitorTx = time.Second * 5
)

type EthClienter interface {
ethereum.LogFilterer
ethereum.BlockNumberReader
ethereum.ChainReader
bind.ContractBackend
}

type EthTxManager interface {
Remove(ctx context.Context, id common.Hash) error
ResultsByStatus(ctx context.Context, statuses []ethtxmanager.MonitoredTxStatus) ([]ethtxmanager.MonitoredTxResult, error)
Result(ctx context.Context, id common.Hash) (ethtxmanager.MonitoredTxResult, error)
Add(ctx context.Context, to *common.Address, forcedNonce *uint64, value *big.Int, data []byte, gasOffset uint64, sidecar *types.BlobTxSidecar) (common.Hash, error)
}

type EVMChainGERSender struct {
gerContract *pessimisticglobalexitroot.Pessimisticglobalexitroot
gerAddr common.Address
sender common.Address
client EthClienter
ethTxMan EthTxManager
gasOffset uint64
}

func NewEVMChainGERSender(
globalExitRoot, sender common.Address,
client EthClienter,
ethTxMan EthTxManager,
gasOffset uint64,
) (*EVMChainGERSender, error) {
gerContract, err := pessimisticglobalexitroot.NewPessimisticglobalexitroot(globalExitRoot, client)
if err != nil {
return nil, err
}
return &EVMChainGERSender{
gerContract: gerContract,
gerAddr: globalExitRoot,
sender: sender,
client: client,
ethTxMan: ethTxMan,
gasOffset: gasOffset,
}, nil
}

func (c *EVMChainGERSender) IsGERAlreadyInjected(ger common.Hash) (bool, error) {
timestamp, err := c.gerContract.GlobalExitRootMap(&bind.CallOpts{Pending: false}, ger)
if err != nil {
return false, err
}
return timestamp.Cmp(big.NewInt(0)) == 0, nil
}

func (c *EVMChainGERSender) UpdateGERWaitUntilMined(ctx context.Context, ger common.Hash) error {
opts := &bind.TransactOpts{
From: c.sender,
// Hardcode dummy values to avoid calls to the client,
// ethtxmanager is going to handle that
NoSend: true,
Nonce: big.NewInt(1),
GasLimit: 1,
GasPrice: big.NewInt(1),
}
tx, err := c.gerContract.UpdateGlobalExitRoot(opts, ger)
if err != nil {
return err
}
id, err := c.ethTxMan.Add(ctx, &c.gerAddr, nil, big.NewInt(0), tx.Data(), c.gasOffset, nil)
if err != nil {
return err
}
for {
time.Sleep(waitPeriodMonitorTx)
res, err := c.ethTxMan.Result(ctx, id)
if err != nil {
log.Error("error calling ethTxMan.Result: ", err)
}
switch res.Status {
case ethtxmanager.MonitoredTxStatusCreated:
continue
case ethtxmanager.MonitoredTxStatusSent:
continue
case ethtxmanager.MonitoredTxStatusFailed:
return fmt.Errorf("tx %s failed", res.ID)
case ethtxmanager.MonitoredTxStatusMined:
return nil
case ethtxmanager.MonitoredTxStatusSafe:
return nil
case ethtxmanager.MonitoredTxStatusFinalized:
return nil
default:
log.Error("unexpected tx status: ", res.Status)
}
}
}
147 changes: 147 additions & 0 deletions aggoracle/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package aggoracle_test

import (
"context"
"math/big"
"strconv"
"testing"
"time"

"github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/globalexitrootnopush0"
"github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/pessimisticglobalexitroot"
"github.com/0xPolygon/cdk/aggoracle"
"github.com/0xPolygon/cdk/aggoracle/chaingersender"
"github.com/0xPolygon/cdk/etherman"
"github.com/0xPolygon/cdk/l1infotreesync"
"github.com/0xPolygon/cdk/reorgdetector"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient/simulated"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

func TestEVM(t *testing.T) {
ctx := context.Background()
l1Client, syncer, gerL1Contract, authL1 := commonSetup(t)
sender := evmSetup(t)
oracle, err := aggoracle.New(sender, l1Client.Client(), syncer, etherman.LatestBlock)
require.NoError(t, err)
oracle.Start(ctx)

runTest(t, gerL1Contract, sender, l1Client, authL1)
}

func commonSetup(t *testing.T) (
*simulated.Backend,
*l1infotreesync.L1InfoTreeSync,
*globalexitrootnopush0.Globalexitrootnopush0,
*bind.TransactOpts,
) {
// Config and spin up
ctx := context.Background()
// Simulated L1
privateKeyL1, err := crypto.GenerateKey()
require.NoError(t, err)
authL1, err := bind.NewKeyedTransactorWithChainID(privateKeyL1, big.NewInt(1337))
require.NoError(t, err)
l1Client, gerL1Addr, gerL1Contract, err := newSimulatedL1(authL1)
require.NoError(t, err)
// Reorg detector
rdm := NewReorgDetectorMock(t)
rdm.On("Subscribe", mock.Anything).Return(&reorgdetector.Subscription{})
rdm.On("AddBlockToTrack", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
reorg, err := reorgdetector.New(ctx)
require.NoError(t, err)
// Syncer
dbPath := t.TempDir()
syncer, err := l1infotreesync.New(ctx, dbPath, gerL1Addr, 10, etherman.LatestBlock, reorg, l1Client.Client(), 32)
require.NoError(t, err)
syncer.Sync(ctx)

return l1Client, syncer, gerL1Contract, authL1
}

func evmSetup(t *testing.T) aggoracle.ChainSender {
privateKeyL2, err := crypto.GenerateKey()
require.NoError(t, err)
authL2, err := bind.NewKeyedTransactorWithChainID(privateKeyL2, big.NewInt(1337))
require.NoError(t, err)
l2Client, gerL2Addr, _, err := newSimulatedEVMAggSovereignChain(authL2)
require.NoError(t, err)
sender, err := chaingersender.NewEVMChainGERSender(gerL2Addr, authL2.From, l2Client.Client(), nil, 0)
require.NoError(t, err)

return sender
}

func newSimulatedL1(auth *bind.TransactOpts) (
client *simulated.Backend,
gerAddr common.Address,
gerContract *globalexitrootnopush0.Globalexitrootnopush0,
err error,
) {
balance, _ := new(big.Int).SetString("10000000000000000000000000", 10) //nolint:gomnd
address := auth.From
genesisAlloc := map[common.Address]types.Account{
address: {
Balance: balance,
},
}
blockGasLimit := uint64(999999999999999999) //nolint:gomnd
client = simulated.NewBackend(genesisAlloc, simulated.WithBlockGasLimit(blockGasLimit))

gerAddr, _, gerContract, err = globalexitrootnopush0.DeployGlobalexitrootnopush0(auth, client.Client(), auth.From, auth.From)

client.Commit()
return
}

func newSimulatedEVMAggSovereignChain(auth *bind.TransactOpts) (
client *simulated.Backend,
gerAddr common.Address,
gerContract *pessimisticglobalexitroot.Pessimisticglobalexitroot,
err error,
) {
balance, _ := new(big.Int).SetString("10000000000000000000000000", 10) //nolint:gomnd
address := auth.From
genesisAlloc := map[common.Address]types.Account{
address: {
Balance: balance,
},
}
blockGasLimit := uint64(999999999999999999) //nolint:gomnd
client = simulated.NewBackend(genesisAlloc, simulated.WithBlockGasLimit(blockGasLimit))

gerAddr, _, gerContract, err = pessimisticglobalexitroot.DeployPessimisticglobalexitroot(auth, client.Client(), auth.From)

client.Commit()
return
}

func runTest(
t *testing.T,
gerL1Contract *globalexitrootnopush0.Globalexitrootnopush0,
sender aggoracle.ChainSender,
l1Client *simulated.Backend,
authL1 *bind.TransactOpts,
) {
for i := 0; i < 10; i++ {
_, err := gerL1Contract.UpdateExitRoot(authL1, common.HexToHash(strconv.Itoa(i)))
require.NoError(t, err)
l1Client.Commit()
time.Sleep(time.Second * 30)
expectedGER, err := gerL1Contract.GetLastGlobalExitRoot(&bind.CallOpts{Pending: false})
require.NoError(t, err)
isInjected, err := sender.IsGERAlreadyInjected(expectedGER)
require.NoError(t, err)
require.True(t, isInjected)
}
}

type ReorgDetector interface {
Subscribe(id string) *reorgdetector.Subscription
AddBlockToTrack(ctx context.Context, id string, blockNum uint64, blockHash common.Hash) error
}
100 changes: 100 additions & 0 deletions aggoracle/oracle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package aggoracle

import (
"context"
"math/big"
"time"

"github.com/0xPolygon/cdk/etherman"
"github.com/0xPolygon/cdk/l1infotreesync"
"github.com/0xPolygon/cdk/log"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)

var (
waitPeriodNextGER = time.Second * 30
)

type EthClienter interface {
ethereum.LogFilterer
ethereum.BlockNumberReader
ethereum.ChainReader
bind.ContractBackend
}

type L1InfoTreer interface {
GetLatestInfoUntilBlock(ctx context.Context, blockNum uint64) (*l1infotreesync.L1InfoTreeLeaf, error)
}

type ChainSender interface {
IsGERAlreadyInjected(ger common.Hash) (bool, error)
UpdateGERWaitUntilMined(ctx context.Context, ger common.Hash) error
}

type AggOracle struct {
ticker *time.Ticker
l1Client EthClienter
l1Info L1InfoTreer
chainSender ChainSender
blockFinality *big.Int
}

func New(
chainSender ChainSender,
l1Client EthClienter,
l1InfoTreeSyncer L1InfoTreer,
blockFinalityType etherman.BlockNumberFinality,
) (*AggOracle, error) {
ticker := time.NewTicker(waitPeriodNextGER)
finality, err := blockFinalityType.ToBlockNum()
if err != nil {
return nil, err
}
return &AggOracle{
ticker: ticker,
l1Client: l1Client,
l1Info: l1InfoTreeSyncer,
chainSender: chainSender,
blockFinality: finality,
}, nil
}

func (a *AggOracle) Start(ctx context.Context) {
for {
select {
case <-a.ticker.C:
gerToInject, err := a.getLastFinalisedGER(ctx)
if err != nil {
log.Error("error calling getLastFinalisedGER: ", err)
continue
}
if alreadyInjectd, err := a.chainSender.IsGERAlreadyInjected(gerToInject); err != nil {
log.Error("error calling isGERAlreadyInjected: ", err)
continue
} else if alreadyInjectd {
log.Debugf("GER %s already injected", gerToInject.Hex())
continue
}
if err := a.chainSender.UpdateGERWaitUntilMined(ctx, gerToInject); err != nil {
log.Error("error calling updateGERWaitUntilMined: ", err)
continue
}
case <-ctx.Done():
return
}
}
}

func (a *AggOracle) getLastFinalisedGER(ctx context.Context) (common.Hash, error) {
header, err := a.l1Client.HeaderByNumber(ctx, a.blockFinality)
if err != nil {
return common.Hash{}, err
}
info, err := a.l1Info.GetLatestInfoUntilBlock(ctx, header.Number.Uint64())
if err != nil {
return common.Hash{}, err
}
return info.GlobalExitRoot, nil
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ module github.com/0xPolygon/cdk
go 1.22.4

require (
github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240627125553-80db9d8a41b5
github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240724124854-785bb2a0e4a2
github.com/0xPolygon/cdk-data-availability v0.0.7
github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d
github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2
github.com/0xPolygonHermez/zkevm-data-streamer v0.2.3-RC4
github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9
github.com/0xPolygonHermez/zkevm-synchronizer-l1 v0.6.1
github.com/ethereum/go-ethereum v1.14.5
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240627125553-80db9d8a41b5 h1:bezOoVvpQvGvf4aCZQpMPFJGKXhhvktpApr+TsD8rdk=
github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240627125553-80db9d8a41b5/go.mod h1:mFlcEjsm2YBBsu8atHJ3zyVnwM+Z/fMXpVmIJge+WVU=
github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240724124854-785bb2a0e4a2 h1:vMRkvTf+sfCKtEDFRGlZ9saDzQIf+LR+ly2OiRyE/2Q=
github.com/0xPolygon/cdk-contracts-tooling v0.0.0-20240724124854-785bb2a0e4a2/go.mod h1:mFlcEjsm2YBBsu8atHJ3zyVnwM+Z/fMXpVmIJge+WVU=
github.com/0xPolygon/cdk-data-availability v0.0.7 h1:i5v2I8uEgHSZ5BjnJs3Dsy1XpKdSvfZbON9D16gSCUg=
github.com/0xPolygon/cdk-data-availability v0.0.7/go.mod h1:qF+xt2gYwBab8XPh3fr6pnNUMEJq/32rmJN0D630hmY=
github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d h1:sxh6hZ2jF/sxxj2jd5o1vuNNCZjYmn4aRG9SRlVaEFs=
github.com/0xPolygon/cdk-rpc v0.0.0-20240419104226-c0a62ba0f49d/go.mod h1:2scWqMMufrQXu7TikDgQ3BsyaKoX8qP26D6E262vSOg=
github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2 h1:XRMTk+W6vtJVGVjuEznfWyNt7HkRkkuSmlN5Y6p60Sc=
github.com/0xPolygonHermez/zkevm-data-streamer v0.2.2/go.mod h1:0QkAXcFa92mFJrCbN3UPUJGJYes851yEgYHLONnaosE=
github.com/0xPolygonHermez/zkevm-data-streamer v0.2.3-RC4 h1:+4K+xSzv0ImbK30B/T9FauNTrTFUmWcNKYhIgwsE4C4=
github.com/0xPolygonHermez/zkevm-data-streamer v0.2.3-RC4/go.mod h1:0QkAXcFa92mFJrCbN3UPUJGJYes851yEgYHLONnaosE=
github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9 h1:vrAezzwTNke6NroDAltGh1k2AJ6ibmZPBsG0bCltbRc=
github.com/0xPolygonHermez/zkevm-ethtx-manager v0.1.9/go.mod h1:pRqfLQVM3nbzdhy3buqjAgcVyNDKAXOHqTSgkwiKpic=
github.com/0xPolygonHermez/zkevm-synchronizer-l1 v0.6.1 h1:pf0lvUJNl5/3AnpnFPvgaYTUoOydVUaqI0hyZGglI2E=
Expand Down
Loading

0 comments on commit 2c6db44

Please sign in to comment.