Skip to content

Commit

Permalink
feat(evm-reader): Read claim acceptance
Browse files Browse the repository at this point in the history
  • Loading branch information
fmoura committed Sep 3, 2024
1 parent ce710f2 commit 25d491b
Show file tree
Hide file tree
Showing 17 changed files with 1,824 additions and 839 deletions.
220 changes: 220 additions & 0 deletions internal/evmreader/claim.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

package evmreader

import (
"cmp"
"context"
"log/slog"

. "github.com/cartesi/rollups-node/internal/node/model"
"github.com/cartesi/rollups-node/pkg/contracts/iconsensus"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)

func (r *EvmReader) checkForClaimStatus(
ctx context.Context,
apps []application,
mostRecentBlockNumber uint64,
) {

slog.Debug("Checking for new Claim Acceptance Events")

// Classify them by lastClaimCheck block
appsIndexedByLastCheck := indexApps(keyByLastClaimCheck, apps)

for lastClaimCheck, apps := range appsIndexedByLastCheck {

appAddresses := appToAddresses(apps)

// Safeguard: Only check blocks starting from the block where the InputBox
// contract was deployed as Inputs can be added to that same block
if lastClaimCheck < r.inputBoxDeploymentBlock {
lastClaimCheck = r.inputBoxDeploymentBlock - 1
}

if mostRecentBlockNumber > lastClaimCheck {

slog.Info("Checking claim acceptance for applications",
"apps", appAddresses,
"last claim check block", lastClaimCheck,
"most recent block", mostRecentBlockNumber)

r.readAndUpdateClaims(ctx, apps, lastClaimCheck, mostRecentBlockNumber)

} else if mostRecentBlockNumber < lastClaimCheck {
slog.Warn(
"Not reading claim acceptance: most recent block is lower than the last processed one", //nolint:lll
"apps", appAddresses,
"last claim check block", lastClaimCheck,
"most recent block", mostRecentBlockNumber,
)
} else {
slog.Info("Not reading claim acceptance: already checked the most recent blocks",
"apps", appAddresses,
"last claim check block", lastClaimCheck,
"most recent block", mostRecentBlockNumber,
)
}

}
}

func (r *EvmReader) readAndUpdateClaims(
ctx context.Context,
apps []application,
lastClaimCheck, mostRecentBlockNumber uint64,
) {

// DISCLAIMER: The current algorithm will only handle Authority.
// To handle Quorum, node needs to handle acceptance events
// that can happen before claim submission

// Classify them by same IConsensusAddress
sameConsensusApps := indexApps(keyByIConsensus, apps)
for iConsensusAddress, apps := range sameConsensusApps {

appAddresses := appToAddresses(apps)

// All these apps shares the same IConsensus
if len(apps) == 0 {
continue
}
consensusContract := apps[0].consensusContract

// Retrieve Claim Acceptance Events from blockchain
appClaimAcceptanceEventMap, err := r.readClaimAcceptanceFromBlockchain(
ctx, consensusContract, appAddresses, lastClaimCheck+1, mostRecentBlockNumber)
if err != nil {
slog.Error("Error reading claim acceptance status",
"apps", apps,
"IConsensus", iConsensusAddress,
"start", lastClaimCheck,
"end", mostRecentBlockNumber,
"error", err)
continue
}

// Check events against Epochs
for app, claimAcceptances := range appClaimAcceptanceEventMap {

epochs := []*Epoch{}
for _, claimAcceptance := range claimAcceptances {

// Get Previous claims and update their statuses
// rejecting all
previousClaims, err := r.repository.GetPreviousSubmittedClaims(
ctx, app, claimAcceptance.LastProcessedBlockNumber.Uint64())
if err != nil {
slog.Error("Error retrieving previous submitted claims",
"app", app, "error", err)
}

for _, previousClaim := range previousClaims {
previousClaim.Status = EpochStatusClaimRejected
epochs = append(epochs, &previousClaim)
slog.Warn("Claim rejected",
"app", app,
"lastBlock", previousClaim.LastBlock,
"hash", previousClaim.ClaimHash)
}

// Get Claim
claim, err := r.repository.GetEpoch(
ctx, calculateEpochIndex(
r.epochLengthCache[app],
claimAcceptance.LastProcessedBlockNumber.Uint64()),
app)
if err != nil {
slog.Error("Error retrieving claim", "app", app, "error", err)
}

// Check Claim
if claim == nil {
slog.Error("Got unknown claim event",
"app", app,
"claim last block", claimAcceptance.LastProcessedBlockNumber,
"hash", claimAcceptance.Claim)
continue
}

// Update claim status
if claimAcceptance.Claim != *claim.ClaimHash {
slog.Warn("Claim Rejected",
"app", app,
"lastBlock", claim.LastBlock,
"hash", claim.ClaimHash)

claim.Status = EpochStatusClaimRejected
epochs = append(epochs, claim)
} else {
slog.Info("Claim Accepted",
"app", app,
"lastBlock", claim.LastBlock,
"hash", claim.ClaimHash)

claim.Status = EpochStatusClaimAccepted
epochs = append(epochs, claim)
}
}

// Store everything
err = r.repository.StoreClaimsTransaction(
ctx, app, epochs, mostRecentBlockNumber)
if err != nil {
slog.Error("Error storing claims", "app", app, "error", err)
continue
}

}

}

}

func (r *EvmReader) readClaimAcceptanceFromBlockchain(
ctx context.Context,
consensusContract ConsensusContract,
appAddresses []common.Address,
startBlock, endBlock uint64,
) (map[common.Address][]*iconsensus.IConsensusClaimAcceptance, error) {
appClaimAcceptanceMap := make(map[common.Address][]*iconsensus.IConsensusClaimAcceptance)
for _, address := range appAddresses {
appClaimAcceptanceMap[address] = []*iconsensus.IConsensusClaimAcceptance{}
}
opts := &bind.FilterOpts{
Context: ctx,
Start: startBlock,
End: &endBlock,
}
claimAcceptanceEvents, err := consensusContract.RetrieveClaimAcceptanceEvents(
opts, appAddresses)
if err != nil {
return nil, err
}
for _, event := range claimAcceptanceEvents {
appClaimAcceptanceMap[event.AppContract] = insertSorted(
sortByLastBlockNumber, appClaimAcceptanceMap[event.AppContract], event)
}
return appClaimAcceptanceMap, nil
}

// keyByLastClaimCheck is a LastClaimCheck key extractor function intended
// to be used with `indexApps` function, see indexApps()
func keyByLastClaimCheck(app application) uint64 {
return app.LastClaimCheckBlock
}

// keyByIConsensus is a IConsensus address key extractor function intended
// to be used with `indexApps` function, see indexApps()
func keyByIConsensus(app application) Address {
return app.IConsensusAddress
}

// sortByLastBlockNumber is a ClaimAcceptance's by last block number sorting function.
// Intended to be used with insertSorted function, see insertSorted()
func sortByLastBlockNumber(a, b *iconsensus.IConsensusClaimAcceptance) int {
return cmp.Compare(a.LastProcessedBlockNumber.Uint64(), b.LastProcessedBlockNumber.Uint64())
}
Loading

0 comments on commit 25d491b

Please sign in to comment.