Skip to content

Commit

Permalink
Use Desired RPC Block Number in Chain Watcher (#720)
Browse files Browse the repository at this point in the history
  • Loading branch information
rauljordan authored Jan 7, 2025
1 parent cb6922f commit a537dac
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 32 deletions.
2 changes: 1 addition & 1 deletion assertions/poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (m *Manager) waitToPostIfNeeded(
}
minPeriodBlocks := m.chain.MinAssertionPeriodBlocks()
for {
latestBlockNumber, err := m.backend.HeaderU64(ctx)
latestBlockNumber, err := m.chain.DesiredHeaderU64(ctx)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions assertions/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (m *Manager) syncAssertions(ctx context.Context) {
return
}
toBlock, err := retry.UntilSucceeds(ctx, func() (uint64, error) {
return m.backend.HeaderU64(ctx)
return m.chain.DesiredHeaderU64(ctx)
})
if err != nil {
log.Error("Could not get header by number", "err", err)
Expand Down Expand Up @@ -89,7 +89,7 @@ func (m *Manager) syncAssertions(ctx context.Context) {
for {
select {
case <-ticker.C:
toBlock, err := m.backend.HeaderU64(ctx)
toBlock, err := m.chain.DesiredHeaderU64(ctx)
if err != nil {
log.Error("Could not get header by number", "err", err)
continue
Expand Down
1 change: 1 addition & 0 deletions chain-abstraction/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ type AssertionChain interface {
GetAssertion(ctx context.Context, opts *bind.CallOpts, id AssertionHash) (Assertion, error)
IsChallengeComplete(ctx context.Context, challengeParentAssertionHash AssertionHash) (bool, error)
Backend() ChainBackend
DesiredHeaderU64(ctx context.Context) (uint64, error)
RollupAddress() common.Address
StakerAddress() common.Address
AssertionStatus(
Expand Down
17 changes: 14 additions & 3 deletions chain-abstraction/sol-implementation/assertion_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func NewAssertionChain(
confirmedChallengesByParentAssertionHash: threadsafe.NewLruSet(1000, threadsafe.LruSetWithMetric[protocol.AssertionHash]("confirmedChallengesByParentAssertionHash")),
averageTimeForBlockCreation: time.Second * 12,
transactor: transactor,
rpcHeadBlockNumber: rpc.FinalizedBlockNumber,
rpcHeadBlockNumber: rpc.LatestBlockNumber,
withdrawalAddress: copiedOpts.From, // Default to the tx opts' sender.
autoDeposit: true,
}
Expand Down Expand Up @@ -303,6 +303,17 @@ func (a *AssertionChain) Backend() protocol.ChainBackend {
return a.backend
}

func (a *AssertionChain) DesiredHeaderU64(ctx context.Context) (uint64, error) {
header, err := a.backend.HeaderByNumber(ctx, big.NewInt(int64(a.rpcHeadBlockNumber)))
if err != nil {
return 0, err
}
if !header.Number.IsUint64() {
return 0, errors.New("block number is not uint64")
}
return header.Number.Uint64(), nil
}

func (a *AssertionChain) GetAssertion(ctx context.Context, opts *bind.CallOpts, assertionHash protocol.AssertionHash) (protocol.Assertion, error) {
var b [32]byte
copy(b[:], assertionHash.Bytes())
Expand Down Expand Up @@ -685,7 +696,7 @@ func TryConfirmingAssertion(
}
for {
var latestHeaderNumber uint64
latestHeaderNumber, err = chain.Backend().HeaderU64(ctx)
latestHeaderNumber, err = chain.DesiredHeaderU64(ctx)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -1015,7 +1026,7 @@ func (a *AssertionChain) AssertionUnrivaledBlocks(ctx context.Context, assertion
// If there is no second child, we simply return the number of blocks
// since the assertion was created and its parent.
if prevNode.SecondChildBlock == 0 {
num, err := a.backend.HeaderU64(ctx)
num, err := a.DesiredHeaderU64(ctx)
if err != nil {
return 0, err
}
Expand Down
26 changes: 14 additions & 12 deletions challenge-manager/chain-watcher/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"context"
"fmt"
"math"
"math/big"
"sync/atomic"
"time"

Expand Down Expand Up @@ -245,7 +246,7 @@ func (w *Watcher) Start(ctx context.Context) {
for {
select {
case <-ticker.C:
toBlock, err := w.backend.HeaderU64(ctx)
toBlock, err := w.chain.DesiredHeaderU64(ctx)
if err != nil {
log.Error("Could not get latest header", "err", err)
continue
Expand Down Expand Up @@ -290,7 +291,7 @@ func (w *Watcher) Start(ctx context.Context) {
// GetRoyalEdges returns all royal, tracked edges in the watcher by assertion
// hash.
func (w *Watcher) GetRoyalEdges(ctx context.Context) (map[protocol.AssertionHash][]*api.JsonTrackedRoyalEdge, error) {
blockNum, err := w.chain.Backend().HeaderU64(ctx)
blockNum, err := w.chain.DesiredHeaderU64(ctx)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -386,7 +387,7 @@ func (w *Watcher) ComputeAncestors(
challengedAssertionHash,
)
}
blockHeaderNumber, err := w.chain.Backend().HeaderU64(ctx)
blockHeaderNumber, err := w.chain.DesiredHeaderU64(ctx)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -421,7 +422,7 @@ func (w *Watcher) IsEssentialAncestorConfirmable(
challengedAssertionHash,
)
}
blockHeaderNumber, err := w.chain.Backend().HeaderU64(ctx)
blockHeaderNumber, err := w.chain.DesiredHeaderU64(ctx)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -453,7 +454,7 @@ func (w *Watcher) IsConfirmableEssentialEdge(
if !ok {
return false, nil, 0, fmt.Errorf("could not get challenge for top level assertion %#x", challengedAssertionHash)
}
blockHeaderNumber, err := w.chain.Backend().HeaderU64(ctx)
blockHeaderNumber, err := w.chain.DesiredHeaderU64(ctx)
if err != nil {
return false, nil, 0, err
}
Expand Down Expand Up @@ -935,23 +936,24 @@ type filterRange struct {
// Gets the start and end block numbers for our filter queries, starting from
// the latest confirmed assertion's block number up to the latest block number.
func (w *Watcher) getStartEndBlockNum(ctx context.Context) (filterRange, error) {
latestBlock, err := w.chain.Backend().HeaderU64(ctx)
desiredRPCBlock := w.chain.GetDesiredRpcHeadBlockNumber()
latestDesiredBlockHeader, err := w.chain.Backend().HeaderByNumber(ctx, big.NewInt(int64(desiredRPCBlock)))
if err != nil {
return filterRange{}, err
}
startBlock := latestBlock
if !latestDesiredBlockHeader.Number.IsUint64() {
return filterRange{}, errors.New("latest desired block number is not a uint64")
}
latestDesiredBlockNum := latestDesiredBlockHeader.Number.Uint64()
startBlock := latestDesiredBlockNum
if w.maxLookbackBlocks < startBlock {
startBlock = startBlock - w.maxLookbackBlocks
} else {
startBlock = 0
}
headerNumber, err := w.backend.HeaderU64(ctx)
if err != nil {
return filterRange{}, err
}
return filterRange{
startBlockNum: startBlock,
endBlockNum: headerNumber,
endBlockNum: latestDesiredBlockNum,
}, nil
}

Expand Down
6 changes: 4 additions & 2 deletions challenge-manager/challenge-tree/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type ComputePathWeightArgs struct {
BlockNum uint64
}

var ErrChildrenNotYetSeen = errors.New("child not yet tracked")

// ComputePathWeight from a child edge to a specified ancestor edge. A weight is the sum of the local timers
// of all edges along the path.
//
Expand Down Expand Up @@ -187,11 +189,11 @@ func (ht *RoyalChallengeTree) findEssentialPaths(
lowerChildId, upperChildId := lowerChildIdOpt.Unwrap(), upperChildIdOpt.Unwrap()
lowerChild, ok := ht.edges.TryGet(lowerChildId)
if !ok {
return nil, nil, fmt.Errorf("lower child not yet tracked")
return nil, nil, errors.Wrap(ErrChildrenNotYetSeen, "lower child")
}
upperChild, ok := ht.edges.TryGet(upperChildId)
if !ok {
return nil, nil, fmt.Errorf("upper child not yet tracked")
return nil, nil, errors.Wrap(ErrChildrenNotYetSeen, "upper child")
}
lowerTimer, err := ht.LocalTimer(ctx, lowerChild, blockNum)
if err != nil {
Expand Down
14 changes: 10 additions & 4 deletions challenge-manager/edge-tracker/tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,21 +232,21 @@ func (et *Tracker) Act(ctx context.Context) error {
case EdgeStarted:
canOsp, err := canOneStepProve(ctx, et.edge)
if err != nil {
log.Error("Could not check if edge can be one step proven", fields, "err", err)
log.Error("Could not check if edge can be one step proven", append(fields, "err", err)...)
et.fsm.MarkError(err)
return et.fsm.Do(edgeBackToStart{})
}
wasConfirmed, err := et.tryToConfirmEdge(ctx)
if err != nil {
log.Error("Could not check if edge can be confirmed from start state", fields, "err", err)
log.Error("Could not check if edge can be confirmed from start state", append(fields, "err", err)...)
et.fsm.MarkError(err)
}
if wasConfirmed {
return et.fsm.Do(edgeAwaitChallengeCompletion{})
}
hasRival, err := et.edge.HasRival(ctx)
if err != nil {
log.Error("Could not check if edge has rival", fields, "err", err)
log.Error("Could not check if edge has rival", append(fields, "err", err)...)
et.fsm.MarkError(err)
return et.fsm.Do(edgeBackToStart{})
}
Expand All @@ -270,7 +270,7 @@ func (et *Tracker) Act(ctx context.Context) error {
case EdgeAtOneStepProof:
ok, err := et.isEssentialAncestorConfirmable(ctx)
if err != nil {
log.Error("Could not check if closest essential ancestor is confirmable", fields, "err", err)
log.Error("Could not check if closest essential ancestor is confirmable", append(fields, "err", err)...)
et.fsm.MarkError(err)
return et.fsm.Do(edgeBackToStart{})
}
Expand Down Expand Up @@ -464,6 +464,12 @@ func (et *Tracker) tryToConfirmEdge(ctx context.Context) (bool, error) {
chalPeriod,
)
if err != nil {
// If the error is that the child edges have not yet been observed by our chain watcher,
// we can simply return false and nil as they will eventually seen. This may occur when the validator
// is relying on safe or finalized data from the chain watcher.
if errors.Is(err, challengetree.ErrChildrenNotYetSeen) {
return false, nil
}
return false, errors.Wrap(err, "not check if essential edge is confirmable")
}
end := time.Since(start)
Expand Down
6 changes: 3 additions & 3 deletions testing/endtoend/e2e_crash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import (
// We cancel the honest validator's context after it opens the first subchallenge and prove that it
// can restart and carry things out to confirm the honest, claimed assertion in the challenge.
func TestEndToEnd_HonestValidatorCrashes(t *testing.T) {
t.Parallel()
neutralCtx, neutralCancel := context.WithCancel(context.Background())
defer neutralCancel()
evilCtx, evilCancel := context.WithCancel(context.Background())
Expand All @@ -41,6 +40,7 @@ func TestEndToEnd_HonestValidatorCrashes(t *testing.T) {
defer honestCancel()

protocolCfg := defaultProtocolParams()
protocolCfg.challengePeriodBlocks = 40
timeCfg := defaultTimeParams()
timeCfg.blockTime = time.Second
inboxCfg := defaultInboxParams()
Expand Down Expand Up @@ -276,8 +276,8 @@ func TestEndToEnd_HonestValidatorCrashes(t *testing.T) {
if sender != txOpts.From {
continue
}
// Skip edges that are not essential roots.
if it.Event.ClaimId == (common.Hash{}) {
// Skip edges that are not essential roots (skip the top-level edge).
if it.Event.ClaimId == (common.Hash{}) || it.Event.Level == 0 {
continue
}
honestEssentialRootIds[it.Event.EdgeId] = false
Expand Down
11 changes: 6 additions & 5 deletions testing/endtoend/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ type protocolParams struct {
func defaultProtocolParams() protocolParams {
return protocolParams{
numBigStepLevels: 1,
challengePeriodBlocks: 40,
challengePeriodBlocks: 15,
layerZeroHeights: protocol.LayerZeroHeights{
BlockChallengeHeight: 1 << 5,
BigStepChallengeHeight: 1 << 5,
SmallStepChallengeHeight: 1 << 5,
BlockChallengeHeight: 1 << 4,
BigStepChallengeHeight: 1 << 4,
SmallStepChallengeHeight: 1 << 4,
},
}
}
Expand Down Expand Up @@ -143,6 +143,7 @@ func TestEndToEnd_MaxWavmOpcodes(t *testing.T) {
BigStepChallengeHeight: 1 << 14,
SmallStepChallengeHeight: 1 << 14,
}
protocolCfg.challengePeriodBlocks = 30
runEndToEndTest(t, &e2eConfig{
backend: simulated,
protocol: protocolCfg,
Expand Down Expand Up @@ -176,10 +177,10 @@ func TestEndToEnd_TwoEvilValidators(t *testing.T) {
}

func TestEndToEnd_ManyEvilValidators(t *testing.T) {
t.Skip("This test is too slow to run in CI")
protocolCfg := defaultProtocolParams()
timeCfg := defaultTimeParams()
timeCfg.assertionPostingInterval = time.Hour
protocolCfg.challengePeriodBlocks = 50
runEndToEndTest(t, &e2eConfig{
backend: simulated,
protocol: protocolCfg,
Expand Down
5 changes: 5 additions & 0 deletions testing/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,11 @@ func (m *MockProtocol) GetDesiredRpcHeadBlockNumber() rpc.BlockNumber {
}

// Read-only methods.
func (m *MockProtocol) DesiredHeaderU64(ctx context.Context) (uint64, error) {
args := m.Called()
return args.Get(0).(uint64), args.Error(1)
}

func (m *MockProtocol) Backend() protocol.ChainBackend {
args := m.Called()
return args.Get(0).(protocol.ChainBackend)
Expand Down

0 comments on commit a537dac

Please sign in to comment.