Skip to content

Commit

Permalink
[NIT-2489] Honest Strategy Revamp to Consider Path Weights (#634)
Browse files Browse the repository at this point in the history
Co-authored-by: Aman Sanghi <[email protected]>
Co-authored-by: amsanghi <[email protected]>
Co-authored-by: Pepper Lebeck-Jobe <[email protected]>
  • Loading branch information
4 people authored Nov 22, 2024
1 parent 3fcf727 commit 218c2a3
Show file tree
Hide file tree
Showing 48 changed files with 1,102 additions and 652 deletions.
2 changes: 1 addition & 1 deletion assertions/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ go_library(
"@com_github_ccoveille_go_safecast//:go-safecast",
"@com_github_ethereum_go_ethereum//accounts/abi/bind",
"@com_github_ethereum_go_ethereum//common",
"@com_github_ethereum_go_ethereum//core/types",
"@com_github_ethereum_go_ethereum//log",
"@com_github_ethereum_go_ethereum//metrics",
"@com_github_ethereum_go_ethereum//rpc",
"@com_github_pkg_errors//:errors",
],
)
Expand Down
7 changes: 4 additions & 3 deletions assertions/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ package assertions

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

"github.com/pkg/errors"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rpc"

"github.com/offchainlabs/bold/api/db"
protocol "github.com/offchainlabs/bold/chain-abstraction"
Expand Down Expand Up @@ -66,7 +67,7 @@ var defaultTimings = timings{
type Manager struct {
stopwaiter.StopWaiter
chain protocol.AssertionChain
backend bind.ContractBackend
backend protocol.ChainBackend
execProvider l2stateprovider.ExecutionProvider
times timings
rollupAddr common.Address
Expand Down Expand Up @@ -228,7 +229,7 @@ func (m *Manager) checkLatestDesiredBlock(ctx context.Context) {
case <-ctx.Done():
return
case <-time.After(time.Minute):
latestSafeBlock, err := m.backend.HeaderByNumber(ctx, m.chain.GetDesiredRpcHeadBlockNumber())
latestSafeBlock, err := m.backend.HeaderByNumber(ctx, big.NewInt(int64(rpc.SafeBlockNumber)))
if err != nil {
log.Error("Error getting latest safe block", "err", err)
continue
Expand Down
3 changes: 2 additions & 1 deletion assertions/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,11 @@ func TestComplexAssertionForkScenario(t *testing.T) {
require.NoError(t, err)
chalManager.Start(ctx)

time.Sleep(time.Second)
time.Sleep(time.Second * 2)

// Assert that Charlie posted the rival assertion at batch 4.
charlieSubmitted := charlieAssertionManager.AssertionsSubmittedInProcess()
require.Equal(t, true, len(charlieSubmitted) > 0)
charlieAssertion := charlieSubmitted[0]
charlieAssertionInfo, err := charlieChain.ReadAssertionCreationInfo(ctx, charlieAssertion)
require.NoError(t, err)
Expand Down
9 changes: 3 additions & 6 deletions assertions/poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,16 +204,13 @@ func (m *Manager) waitToPostIfNeeded(
ctx context.Context,
parentCreationInfo *protocol.AssertionCreatedInfo,
) error {
latestBlock, err := m.backend.HeaderByNumber(ctx, m.chain.GetDesiredRpcHeadBlockNumber())
latestBlockNumber, err := m.backend.HeaderU64(ctx)
if err != nil {
return err
}
if !latestBlock.Number.IsUint64() {
return errors.New("latest block number not a uint64")
}
blocksSinceLast := uint64(0)
if parentCreationInfo.CreationBlock < latestBlock.Number.Uint64() {
blocksSinceLast = latestBlock.Number.Uint64() - parentCreationInfo.CreationBlock
if parentCreationInfo.CreationBlock < latestBlockNumber {
blocksSinceLast = latestBlockNumber - parentCreationInfo.CreationBlock
}
minPeriodBlocks, err := m.chain.MinAssertionPeriodBlocks(ctx)
if err != nil {
Expand Down
17 changes: 3 additions & 14 deletions assertions/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"

"github.com/offchainlabs/bold/api"
Expand Down Expand Up @@ -57,18 +56,13 @@ func (m *Manager) syncAssertions(ctx context.Context) {
log.Error("Could not get rollup user logic filterer", "err", err)
return
}
latestBlock, err := retry.UntilSucceeds(ctx, func() (*gethtypes.Header, error) {
return m.backend.HeaderByNumber(ctx, m.chain.GetDesiredRpcHeadBlockNumber())
toBlock, err := retry.UntilSucceeds(ctx, func() (uint64, error) {
return m.backend.HeaderU64(ctx)
})
if err != nil {
log.Error("Could not get header by number", "err", err)
return
}
if !latestBlock.Number.IsUint64() {
log.Error("Latest block number was not a uint64")
return
}
toBlock := latestBlock.Number.Uint64()
if fromBlock != toBlock {
filterOpts := &bind.FilterOpts{
Start: fromBlock,
Expand All @@ -95,16 +89,11 @@ func (m *Manager) syncAssertions(ctx context.Context) {
for {
select {
case <-ticker.C:
latestBlock, err := m.backend.HeaderByNumber(ctx, m.chain.GetDesiredRpcHeadBlockNumber())
toBlock, err := m.backend.HeaderU64(ctx)
if err != nil {
log.Error("Could not get header by number", "err", err)
continue
}
if !latestBlock.Number.IsUint64() {
log.Error("Latest block number was not a uint64")
continue
}
toBlock := latestBlock.Number.Uint64()
if fromBlock == toBlock {
continue
}
Expand Down
1 change: 1 addition & 0 deletions chain-abstraction/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ go_library(
"@com_github_ethereum_go_ethereum//common",
"@com_github_ethereum_go_ethereum//core/types",
"@com_github_ethereum_go_ethereum//crypto",
"@com_github_ethereum_go_ethereum//rpc",
],
)
35 changes: 24 additions & 11 deletions chain-abstraction/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rpc"

"github.com/offchainlabs/bold/containers/option"
"github.com/offchainlabs/bold/solgen/go/rollupgen"
Expand All @@ -34,6 +35,13 @@ type ChainBackend interface {
ReceiptFetcher
TxFetcher
HeadSubscriber
ChainID(ctx context.Context) (*big.Int, error)
Close()
Client() rpc.ClientInterface
// HeaderU64 returns either latest, safe, or finalized block number from
// the current canonical chain, depending on how the underlying implementation
// of ChainBackend is configured.
HeaderU64(ctx context.Context) (uint64, error)
}

// ReceiptFetcher defines the ability to retrieve transactions receipts from the chain.
Expand Down Expand Up @@ -149,7 +157,6 @@ type AssertionChain interface {
ctx context.Context, id AssertionHash,
) (*AssertionCreatedInfo, error)
GetCallOptsWithDesiredRpcHeadBlockNumber(opts *bind.CallOpts) *bind.CallOpts
GetDesiredRpcHeadBlockNumber() *big.Int

MinAssertionPeriodBlocks(ctx context.Context) (uint64, error)
AssertionUnrivaledBlocks(ctx context.Context, assertionHash AssertionHash) (uint64, error)
Expand Down Expand Up @@ -409,25 +416,31 @@ type ReadOnlyEdge interface {
TopLevelClaimHeight(ctx context.Context) (OriginHeights, error)
}

// SpecEdge according to the protocol specification.
type SpecEdge interface {
ReadOnlyEdge
MarkAsHonest()
AsVerifiedHonest() (VerifiedRoyalEdge, bool)
}

// VerifiedRoyalEdge marks edges that are known to be royal. For example,
// when a local validator creates an edge, it is known to be royal and several types
// expensive or duplicate computation can be avoided in methods that take in this type.
// A sentinel method `Honest()` is used to mark an edge as satisfying this interface.
type VerifiedRoyalEdge interface {
SpecEdge
Honest()
}

// SpecEdge according to the protocol specification.
type SpecEdge interface {
ReadOnlyEdge
// Bisection capabilities for an edge. Returns the two child
// edges that are created as a result.
// Bisect defines a method to bisect an edge into two children.
// Returns the two child edges that are created as a result.
// Only honest edges should be bisected.
Bisect(
ctx context.Context,
prefixHistoryRoot common.Hash,
prefixProof []byte,
) (VerifiedRoyalEdge, VerifiedRoyalEdge, error)
// Confirms an edge for having a total timer >= one challenge period.
ConfirmByTimer(ctx context.Context) (*types.Transaction, error)
// ConfirmByTimer confirms an edge for having a total timer >= one challenge period.
// The claimed assertion hash the edge corresponds to is required as part of the onchain
// transaction to confirm the edge.
// Only honest edges should be confirmed by timer.
ConfirmByTimer(ctx context.Context, claimedAssertion AssertionHash) (*types.Transaction, error)
Honest()
}
1 change: 1 addition & 0 deletions chain-abstraction/sol-implementation/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ go_test(
"@com_github_ethereum_go_ethereum//accounts/abi/bind",
"@com_github_ethereum_go_ethereum//common",
"@com_github_ethereum_go_ethereum//core/types",
"@com_github_ethereum_go_ethereum//rpc",
"@com_github_stretchr_testify//require",
],
)
37 changes: 14 additions & 23 deletions chain-abstraction/sol-implementation/assertion_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,6 @@ func init() {
}
}

// ChainBackend to interact with the underlying blockchain.
type ChainBackend interface {
bind.ContractBackend
ReceiptFetcher
}

// ReceiptFetcher defines the ability to retrieve transactions receipts from the chain.
type ReceiptFetcher interface {
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
Expand All @@ -88,7 +82,7 @@ type Transactor interface {
}

type ChainBackendTransactor struct {
ChainBackend
protocol.ChainBackend
fifo *FIFO
}

Expand Down Expand Up @@ -541,25 +535,22 @@ func TryConfirmingAssertion(
return true, nil
}
for {
var latestHeader *types.Header
latestHeader, err = chain.Backend().HeaderByNumber(ctx, chain.GetDesiredRpcHeadBlockNumber())
var latestHeaderNumber uint64
latestHeaderNumber, err = chain.Backend().HeaderU64(ctx)
if err != nil {
return false, err
}
if !latestHeader.Number.IsUint64() {
return false, errors.New("latest block number is not a uint64")
}
confirmable := latestHeader.Number.Uint64() >= confirmableAfterBlock
confirmable := latestHeaderNumber >= confirmableAfterBlock

// If the assertion is not yet confirmable, we can simply wait.
if !confirmable {
var blocksLeftForConfirmation int64
if confirmableAfterBlock > latestHeader.Number.Uint64() {
if latestHeaderNumber > confirmableAfterBlock {
blocksLeftForConfirmation = 0
} else {
blocksLeftForConfirmation, err = safecast.ToInt64(confirmableAfterBlock - latestHeader.Number.Uint64())
blocksLeftForConfirmation, err = safecast.ToInt64(confirmableAfterBlock - latestHeaderNumber)
if err != nil {
return false, errors.Wrap(err, "could not convert blocks left for confirmation to int64")
return false, err
}
}
timeToWait := averageTimeForBlockCreation * time.Duration(blocksLeftForConfirmation)
Expand Down Expand Up @@ -871,14 +862,10 @@ 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 {
latestHeader, err := a.backend.HeaderByNumber(ctx, a.GetDesiredRpcHeadBlockNumber())
num, err := a.backend.HeaderU64(ctx)
if err != nil {
return 0, err
}
if !latestHeader.Number.IsUint64() {
return 0, errors.New("latest header number is not a uint64")
}
num := latestHeader.Number.Uint64()

// Should never happen.
if wantNode.CreatedAtBlock > num {
Expand Down Expand Up @@ -1039,11 +1026,15 @@ func (a *AssertionChain) GetCallOptsWithDesiredRpcHeadBlockNumber(opts *bind.Cal
return opts
}

func (a *AssertionChain) GetDesiredRpcHeadBlockNumber() *big.Int {
func (a *AssertionChain) GetCallOptsWithSafeBlockNumber(opts *bind.CallOpts) *bind.CallOpts {
if opts == nil {
opts = &bind.CallOpts{}
}
// If we are running tests, we want to use the latest block number since
// simulated backends only support the latest block number.
if flag.Lookup("test.v") != nil {
return nil
}
return big.NewInt(int64(a.rpcHeadBlockNumber))
opts.BlockNumber = big.NewInt(int64(rpc.SafeBlockNumber))
return opts
}
8 changes: 4 additions & 4 deletions chain-abstraction/sol-implementation/assertion_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func TestConfirmAssertionByChallengeWinner(t *testing.T) {
challengeManager := createdData.Chains[0].SpecChallengeManager()

// Honest assertion being added.
leafAdder := func(stateManager l2stateprovider.Provider, leaf protocol.Assertion) protocol.SpecEdge {
leafAdder := func(stateManager l2stateprovider.Provider, leaf protocol.Assertion) protocol.VerifiedRoyalEdge {
assertionMetadata := &l2stateprovider.AssociatedAssertionMetadata{
WasmModuleRoot: common.Hash{},
FromState: protocol.GoGlobalState{
Expand Down Expand Up @@ -367,7 +367,7 @@ func TestConfirmAssertionByChallengeWinner(t *testing.T) {
require.ErrorContains(t, err, "EDGE_NOT_CONFIRMED")
})
t.Run("level zero block edge confirmed allows assertion confirmation", func(t *testing.T) {
_, err = honestEdge.ConfirmByTimer(ctx)
_, err = honestEdge.ConfirmByTimer(ctx, createdData.Leaf1.Id())
require.NoError(t, err)

// Adjust beyond the grace period.
Expand Down Expand Up @@ -428,7 +428,7 @@ func TestIsChallengeComplete(t *testing.T) {
challengeManager := createdData.Chains[0].SpecChallengeManager()

// Honest assertion being added.
leafAdder := func(stateManager l2stateprovider.Provider, leaf protocol.Assertion) protocol.SpecEdge {
leafAdder := func(stateManager l2stateprovider.Provider, leaf protocol.Assertion) protocol.VerifiedRoyalEdge {
assertionMetadata := &l2stateprovider.AssociatedAssertionMetadata{
WasmModuleRoot: common.Hash{},
FromState: protocol.GoGlobalState{
Expand Down Expand Up @@ -487,7 +487,7 @@ func TestIsChallengeComplete(t *testing.T) {
createdData.Backend.Commit()
}

_, err = honestEdge.ConfirmByTimer(ctx)
_, err = honestEdge.ConfirmByTimer(ctx, createdData.Leaf1.Id())
require.NoError(t, err)

// Adjust beyond the grace period.
Expand Down
Loading

0 comments on commit 218c2a3

Please sign in to comment.