diff --git a/aggoracle/chaingersender/evm.go b/aggoracle/chaingersender/evm.go new file mode 100644 index 00000000..c7860528 --- /dev/null +++ b/aggoracle/chaingersender/evm.go @@ -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) + } + } +} diff --git a/aggoracle/e2e_test.go b/aggoracle/e2e_test.go new file mode 100644 index 00000000..04d16cb0 --- /dev/null +++ b/aggoracle/e2e_test.go @@ -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 +} diff --git a/aggoracle/oracle.go b/aggoracle/oracle.go new file mode 100644 index 00000000..0e89a7f2 --- /dev/null +++ b/aggoracle/oracle.go @@ -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 +} diff --git a/go.mod b/go.mod index 18711d78..e8a816e9 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 8eff1443..b2d9e4b6 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/l1infotreesync/downloader.go b/l1infotreesync/downloader.go index a8ca4cc2..549e482e 100644 --- a/l1infotreesync/downloader.go +++ b/l1infotreesync/downloader.go @@ -15,11 +15,8 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) -const ( - waitForNewBlocksPeriod = time.Millisecond * 100 -) - var ( + waitForNewBlocksPeriod = time.Millisecond * 100 updateL1InfoTreeSignature = crypto.Keccak256Hash([]byte("UpdateL1InfoTree(bytes32,bytes32)")) ) @@ -201,7 +198,7 @@ func (d *downloaderImplementation) appendLog(b *block, l types.Log) { } b.Events = append(b.Events, L1InfoTreeUpdate{ MainnetExitRoot: l1InfoTreeUpdate.MainnetExitRoot, - RollupExitRoot: l1InfoTreeUpdate.MainnetExitRoot, + RollupExitRoot: l1InfoTreeUpdate.RollupExitRoot, }) default: log.Fatalf("unexpected log %+v", l) diff --git a/l1infotreesync/e2e_test.go b/l1infotreesync/e2e_test.go index 5e97e92a..baad1d53 100644 --- a/l1infotreesync/e2e_test.go +++ b/l1infotreesync/e2e_test.go @@ -2,25 +2,28 @@ package l1infotreesync import ( "context" + "fmt" "math/big" "strconv" "testing" "time" - "github.com/0xPolygon/cdk-contracts-tooling/contracts/elderberry/polygonzkevmglobalexitrootv2" + "github.com/0xPolygon/cdk-contracts-tooling/contracts/manual/globalexitrootnopush0" "github.com/0xPolygon/cdk/etherman" + "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 newSimulatedClient(auth *bind.TransactOpts) ( client *simulated.Backend, gerAddr common.Address, - gerContract *polygonzkevmglobalexitrootv2.Polygonzkevmglobalexitrootv2, + gerContract *globalexitrootnopush0.Globalexitrootnopush0, err error, ) { balance, _ := new(big.Int).SetString("10000000000000000000000000", 10) //nolint:gomnd @@ -33,13 +36,14 @@ func newSimulatedClient(auth *bind.TransactOpts) ( blockGasLimit := uint64(999999999999999999) //nolint:gomnd client = simulated.NewBackend(genesisAlloc, simulated.WithBlockGasLimit(blockGasLimit)) - gerAddr, _, gerContract, err = polygonzkevmglobalexitrootv2.DeployPolygonzkevmglobalexitrootv2(auth, client.Client(), auth.From, auth.From) + gerAddr, _, gerContract, err = globalexitrootnopush0.DeployGlobalexitrootnopush0(auth, client.Client(), auth.From, auth.From) client.Commit() return } func TestE2E(t *testing.T) { + waitForNewBlocksPeriod = time.Millisecond ctx := context.Background() dbPath := t.TempDir() privateKey, err := crypto.GenerateKey() @@ -47,25 +51,33 @@ func TestE2E(t *testing.T) { auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1337)) require.NoError(t, err) rdm := NewReorgDetectorMock(t) + rdm.On("Subscribe", reorgDetectorID).Return(&reorgdetector.Subscription{}) + rdm.On("AddBlockToTrack", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) client, gerAddr, gerSc, err := newSimulatedClient(auth) + require.NoError(t, err) syncer, err := New(ctx, dbPath, gerAddr, 10, etherman.LatestBlock, rdm, client.Client(), 32) require.NoError(t, err) go syncer.Sync(ctx) // Update GER 10 times + // TODO: test syncer restart for i := 0; i < 10; i++ { - gerSc.UpdateExitRoot(auth, common.HexToHash(strconv.Itoa(i))) + _, err := gerSc.UpdateExitRoot(auth, common.HexToHash(strconv.Itoa(i))) + require.NoError(t, err) client.Commit() // Let the processor catch up - time.Sleep(time.Millisecond * 100) + time.Sleep(time.Millisecond * 10) - expectedRoot, err := gerSc.GetRoot(nil) + expectedRoot, err := gerSc.GetRoot(&bind.CallOpts{Pending: false}) require.NoError(t, err) - info, err := syncer.GetInfoByRoot(ctx, expectedRoot) + expectedGER, err := gerSc.GetLastGlobalExitRoot(&bind.CallOpts{Pending: false}) require.NoError(t, err) - require.Equal(t, expectedRoot, info.L1InfoTreeRoot) info2, err := syncer.GetInfoByIndex(ctx, uint32(i)) - require.NoError(t, err) + require.NoError(t, err, fmt.Sprintf("index: %d, root: %s", i, common.Bytes2Hex(expectedRoot[:]))) + require.Equal(t, common.Hash(expectedGER), info2.GlobalExitRoot, fmt.Sprintf("index: %d", i)) + info, err := syncer.GetInfoByRoot(ctx, expectedRoot) + require.NoError(t, err, fmt.Sprintf("index: %d, expected root: %s, actual root: %s", i, common.Bytes2Hex(expectedRoot[:]), info2.L1InfoTreeRoot)) + require.Equal(t, common.Hash(expectedRoot), info.L1InfoTreeRoot) require.Equal(t, info, info2) } } diff --git a/l1infotreesync/l1infotreesync.go b/l1infotreesync/l1infotreesync.go index 8a5ffde1..fa04f0e6 100644 --- a/l1infotreesync/l1infotreesync.go +++ b/l1infotreesync/l1infotreesync.go @@ -26,14 +26,14 @@ func New( syncBlockChunkSize uint64, blockFinalityType etherman.BlockNumberFinality, rd ReorgDetector, - l2Client EthClienter, + l1Client EthClienter, treeHeight uint8, ) (*L1InfoTreeSync, error) { p, err := newProcessor(ctx, dbPath, treeHeight) if err != nil { return nil, err } - dwn, err := newDownloader(globalExitRoot, l2Client, syncBlockChunkSize, blockFinalityType) + dwn, err := newDownloader(globalExitRoot, l1Client, syncBlockChunkSize, blockFinalityType) if err != nil { return nil, err } diff --git a/l1infotreesync/processor.go b/l1infotreesync/processor.go index 267ec0b4..12047d77 100644 --- a/l1infotreesync/processor.go +++ b/l1infotreesync/processor.go @@ -55,9 +55,9 @@ type storeLeaf struct { type blockWithLeafs struct { // inclusive - firstIndex uint32 + FirstIndex uint32 // not inclusive - lastIndex uint32 + LastIndex uint32 } func (l *storeLeaf) GlobalExitRoot() common.Hash { @@ -111,7 +111,7 @@ func (p *processor) getAllLeavesHashed(ctx context.Context) ([][32]byte, error) defer tx.Rollback() index, err := p.getLastIndex(tx) - if err == ErrNotFound { + if err == ErrNotFound || index == 0 { return nil, nil } if err != nil { @@ -204,7 +204,7 @@ func (p *processor) GetLatestInfoUntilBlock(ctx context.Context, blockNum uint64 if err := json.Unmarshal(v, &blk); err != nil { return nil, err } - hash, err := tx.GetOne(indexTable, uint32ToBytes(blk.lastIndex-1)) + hash, err := tx.GetOne(indexTable, uint32ToBytes(blk.LastIndex-1)) if err != nil { return nil, err } @@ -224,7 +224,7 @@ func (p *processor) GetInfoByIndex(ctx context.Context, index uint32) (*L1InfoTr } func (p *processor) getInfoByIndexWithTx(tx kv.Tx, index uint32) (*L1InfoTreeLeaf, error) { - hash, err := tx.GetOne(rootTable, uint32ToBytes(index)) + hash, err := tx.GetOne(indexTable, uint32ToBytes(index)) if err != nil { return nil, err } @@ -309,7 +309,7 @@ func (p *processor) reorg(firstReorgedBlock uint64) error { tx.Rollback() return err } - for i := blk.firstIndex; i < blk.lastIndex; i++ { + for i := blk.FirstIndex; i < blk.LastIndex; i++ { if err := p.deleteLeaf(tx, i); err != nil { tx.Rollback() return err @@ -370,12 +370,16 @@ func (p *processor) processBlock(b block) error { return err } if len(b.Events) > 0 { + var initialIndex uint32 lastIndex, err := p.getLastIndex(tx) - if err != nil { + if err == ErrNotFound { + initialIndex = 0 + } else if err != nil { tx.Rollback() return err + } else { + initialIndex = lastIndex + 1 } - initialIndex := lastIndex + 1 for i, l := range b.Events { leafToStore := storeLeaf{ Index: initialIndex + uint32(i), @@ -391,8 +395,8 @@ func (p *processor) processBlock(b block) error { } } bwl := blockWithLeafs{ - firstIndex: initialIndex, - lastIndex: initialIndex + uint32(len(b.Events)), + FirstIndex: initialIndex, + LastIndex: initialIndex + uint32(len(b.Events)), } blockValue, err := json.Marshal(bwl) if err != nil { @@ -416,6 +420,9 @@ func (p *processor) getLastIndex(tx kv.Tx) (uint32, error) { if err != nil { return 0, err } + if bNum == 0 { + return 0, nil + } iter, err := tx.RangeDescend(blockTable, uint64ToBytes(bNum), uint64ToBytes(0), 1) if err != nil { return 0, err @@ -431,7 +438,7 @@ func (p *processor) getLastIndex(tx kv.Tx) (uint32, error) { if err := json.Unmarshal(blkBytes, &blk); err != nil { return 0, err } - return blk.lastIndex - 1, nil + return blk.LastIndex - 1, nil } func (p *processor) addLeaf(tx kv.RwTx, leaf storeLeaf) error {