Skip to content

Commit

Permalink
consortium: enable snap sync on Ronin (#389)
Browse files Browse the repository at this point in the history
* consortium: avoid reading statedb in block header verification path

In snap sync, we only verify header without processing block transactions so the
statedb is not available. As a result, we need to avoid reading statedb in the
header verification path and must use the information from header instead.

This commit disables signer list verification in extra data of checkpoint block
header in consortium version 1 when snap/fast sync. In consortium version 2, at
the forked block, when getting the list of validators, we read this information
from header instead of contract, it is expected to behave the same as before.
This commit does not change the header verification process when full sync.

* eth/protocols/snap: fix problems due to idle-but-busy peers

* eth/protocols/snap: throttle trie heal requests when peers DoS us (#25666)

* eth/protocols/snap: throttle trie heal requests when peers DoS us

* eth/protocols/snap: lower heal throttle log to debug

Co-authored-by: Martin Holst Swende <martin@swende.se>

* eth/protocols/snap: fix comment

Co-authored-by: Martin Holst Swende <martin@swende.se>

* eth/protocols/snap: sort trienode heal requests by path (#24779)

* sort snap trienode heal requests

* eth/protocols/snap: remove debug code

* eth/protocols/snap: simplify sort, generate pathsets later

* eth/protocols/snap: review concern

* eth/protocols/snap: renamings

* eth/protocols/snap: add comments in Merge

* eth/protocols/snap: remove variable 'last' in Merge

* eth/protocols/snap: fix lint flaws in test

Co-authored-by: Felix Lange <fjl@twurst.com>

* eth/ethconfig: make full sync the default sync mode

Make full sync the default sync mode until we thoroughly test the snap sync.

---------

Co-authored-by: Martin Holst Swende <martin@swende.se>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
Co-authored-by: Felix Lange <fjl@twurst.com>
  • Loading branch information
4 people authored Jun 18, 2024
1 parent d27eb42 commit 8778779
Show file tree
Hide file tree
Showing 10 changed files with 418 additions and 61 deletions.
4 changes: 0 additions & 4 deletions cmd/ronin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,6 @@ func prepare(ctx *cli.Context) {
case !ctx.IsSet(utils.NetworkIdFlag.Name):
log.Info("Starting Geth on Ethereum mainnet...")
}
// remove snap out of ronin
if ctx.String(utils.SyncModeFlag.Name) == "snap" {
ctx.Set(utils.SyncModeFlag.Name, "full")
}
// If we're a full node on mainnet without --cache specified, bump default cache allowance
if ctx.String(utils.SyncModeFlag.Name) != "light" && !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
// Make sure we're not on any supported preconfigured testnet either
Expand Down
2 changes: 1 addition & 1 deletion consensus/consortium/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ type BaseSnapshot struct {
// ConsortiumAdapter defines a small collection of methods needed to access the private
// methods between consensus engines
type ConsortiumAdapter interface {
GetSnapshot(chain consensus.ChainHeaderReader, number uint64, parents []*types.Header) *BaseSnapshot
GetSnapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) *BaseSnapshot
}
10 changes: 7 additions & 3 deletions consensus/consortium/v1/consortium.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,9 +721,13 @@ func (c *Consortium) CalcDifficulty(chain consensus.ChainHeaderReader, time uint
return c.doCalcDifficulty(c.val, number, validators)
}

func (c *Consortium) GetSnapshot(chain consensus.ChainHeaderReader, number uint64, parents []*types.Header) *consortiumCommon.BaseSnapshot {
header := chain.GetHeaderByNumber(number)
snap, err := c.snapshot(chain, number, header.Hash(), parents)
func (c *Consortium) GetSnapshot(
chain consensus.ChainHeaderReader,
number uint64,
hash common.Hash,
parents []*types.Header,
) *consortiumCommon.BaseSnapshot {
snap, err := c.snapshot(chain, number, hash, parents)
if err != nil {
return nil
}
Expand Down
61 changes: 58 additions & 3 deletions consensus/consortium/v2/consortium.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,11 @@ func (c *Consortium) verifyValidatorFieldsInExtraData(
// in a batch of parents (ascending order) to avoid looking those up from the
// database. This is useful for concurrently verifying a batch of new headers.
func (c *Consortium) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
var (
snap *Snapshot
err error
)

// The genesis block is the always valid dead-end
number := header.Number.Uint64()
if number == 0 {
Expand Down Expand Up @@ -498,8 +503,11 @@ func (c *Consortium) verifyCascadingFields(chain consensus.ChainHeaderReader, he
}
}

// Retrieve the snapshot needed to verify this header and cache it
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
if number == c.forkedBlock {
snap, err = c.snapshotAtConsortiumFork(chain, number-1, header.ParentHash, header, parents)
} else {
snap, err = c.snapshot(chain, number-1, header.ParentHash, parents)
}
if err != nil {
return err
}
Expand All @@ -511,6 +519,53 @@ func (c *Consortium) verifyCascadingFields(chain consensus.ChainHeaderReader, he
return c.verifySeal(chain, header, parents, snap)
}

// snapshotAtConsortiumFork is expected to have to the same
// behavior as snapshot. However, the validator list is read from
// header instead of statedb to support snap sync. We try to keep
// the snapshot function unchanged to minimize the changing effect.
func (c *Consortium) snapshotAtConsortiumFork(
chain consensus.ChainHeaderReader,
number uint64,
hash common.Hash,
forkedHeader *types.Header,
parents []*types.Header,
) (*Snapshot, error) {
var validators []common.Address

snap, err := loadSnapshot(c.config, c.signatures, c.db, hash, c.ethAPI, c.chainConfig)
if err == nil {
log.Trace("Loaded snapshot from disk", "number", number, "hash", hash.Hex())
return snap, nil
}

extraData, err := finality.DecodeExtra(forkedHeader.Extra, false)
if err != nil {
return nil, err
}

for _, validator := range extraData.CheckpointValidators {
validators = append(validators, validator.Address)
}

snap = newSnapshot(c.chainConfig, c.config, c.signatures, number, hash, validators, nil, c.ethAPI)

// load v1 recent list to prevent recent producing-block-validators produce block again
snapV1 := c.v1.GetSnapshot(chain, number, hash, parents)
if snapV1 == nil {
return nil, errors.New("snapshot v1 is not available")
}

snap.Recents = consortiumCommon.RemoveOutdatedRecents(snapV1.Recents, number)

if err := snap.store(c.db); err != nil {
return nil, err
}
log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash)
figure.NewColorFigure("Welcome to DPOS", "", "green", true).Print()

return snap, nil
}

// snapshot retrieves the authorization snapshot at a given point in time.
func (c *Consortium) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
// Search for a snapshot in memory or on disk for checkpoints
Expand Down Expand Up @@ -561,7 +616,7 @@ func (c *Consortium) snapshot(chain consensus.ChainHeaderReader, number uint64,
snap = newSnapshot(c.chainConfig, c.config, c.signatures, number, hash, validators, nil, c.ethAPI)

// load v1 recent list to prevent recent producing-block-validators produce block again
snapV1 := c.v1.GetSnapshot(chain, number, parents)
snapV1 := c.v1.GetSnapshot(chain, number, hash, parents)

// NOTE(linh): In version 1, the snapshot is not used correctly, so we must clean up
// incorrect data in the recent list before going to version 2
Expand Down
3 changes: 2 additions & 1 deletion eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
log.Info("Unprotected transactions allowed")
}
ethAPI := ethapi.NewPublicBlockChainAPI(eth.APIBackend)
eth.engine = ethconfig.CreateConsensusEngine(stack, chainConfig, &ethashConfig, config.Miner.Notify, config.Miner.Noverify, chainDb, ethAPI)
eth.engine = ethconfig.CreateConsensusEngine(stack, chainConfig, &ethashConfig, config.Miner.Notify, config.Miner.Noverify,
chainDb, ethAPI, config.SyncMode)

bcVersion := rawdb.ReadDatabaseVersion(chainDb)
var dbVer = "<nil>"
Expand Down
9 changes: 7 additions & 2 deletions eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ var LightClientGPO = gasprice.Config{

// Defaults contains default settings for use on the Ethereum main net.
var Defaults = Config{
SyncMode: downloader.SnapSync,
SyncMode: downloader.FullSync,
Ethash: ethash.Config{
CacheDir: "ethash",
CachesInMem: 2,
Expand Down Expand Up @@ -233,13 +233,18 @@ func CreateConsensusEngine(
noverify bool,
db ethdb.Database,
ee *ethapi.PublicBlockChainAPI,
syncMode downloader.SyncMode,
) consensus.Engine {
// If proof-of-authority is requested, set it up
if chainConfig.Clique != nil {
return clique.New(chainConfig.Clique, db)
}
if chainConfig.Consortium != nil {
return consortium.New(chainConfig, db, ee, false)
if syncMode == downloader.SnapSync {
return consortium.New(chainConfig, db, ee, true)
} else {
return consortium.New(chainConfig, db, ee, false)
}
}
// Otherwise assume proof-of-work
switch config.PowMode {
Expand Down
109 changes: 109 additions & 0 deletions eth/protocols/snap/sort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2022 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package snap

import (
"bytes"
"fmt"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/trie"
)

func hexToNibbles(s string) []byte {
if len(s) >= 2 && s[0] == '0' && s[1] == 'x' {
s = s[2:]
}
var s2 []byte
for _, ch := range []byte(s) {
s2 = append(s2, '0')
s2 = append(s2, ch)
}
return common.Hex2Bytes(string(s2))
}

func TestRequestSorting(t *testing.T) {

// - Path 0x9 -> {0x19}
// - Path 0x99 -> {0x0099}
// - Path 0x01234567890123456789012345678901012345678901234567890123456789019 -> {0x0123456789012345678901234567890101234567890123456789012345678901, 0x19}
// - Path 0x012345678901234567890123456789010123456789012345678901234567890199 -> {0x0123456789012345678901234567890101234567890123456789012345678901, 0x0099}
var f = func(path string) (trie.SyncPath, TrieNodePathSet, common.Hash) {
data := hexToNibbles(path)
sp := trie.NewSyncPath(data)
tnps := TrieNodePathSet([][]byte(sp))
hash := common.Hash{}
return sp, tnps, hash
}
var (
hashes []common.Hash
paths []trie.SyncPath
pathsets []TrieNodePathSet
)
for _, x := range []string{
"0x9",
"0x012345678901234567890123456789010123456789012345678901234567890195",
"0x012345678901234567890123456789010123456789012345678901234567890197",
"0x012345678901234567890123456789010123456789012345678901234567890196",
"0x99",
"0x012345678901234567890123456789010123456789012345678901234567890199",
"0x01234567890123456789012345678901012345678901234567890123456789019",
"0x0123456789012345678901234567890101234567890123456789012345678901",
"0x01234567890123456789012345678901012345678901234567890123456789010",
"0x01234567890123456789012345678901012345678901234567890123456789011",
} {
sp, tnps, hash := f(x)
hashes = append(hashes, hash)
paths = append(paths, sp)
pathsets = append(pathsets, tnps)
}
_, paths, pathsets = sortByAccountPath(hashes, paths)
{
var b = new(bytes.Buffer)
for i := 0; i < len(paths); i++ {
fmt.Fprintf(b, "\n%d. paths %x", i, paths[i])
}
want := `
0. paths [0099]
1. paths [0123456789012345678901234567890101234567890123456789012345678901 00]
2. paths [0123456789012345678901234567890101234567890123456789012345678901 0095]
3. paths [0123456789012345678901234567890101234567890123456789012345678901 0096]
4. paths [0123456789012345678901234567890101234567890123456789012345678901 0097]
5. paths [0123456789012345678901234567890101234567890123456789012345678901 0099]
6. paths [0123456789012345678901234567890101234567890123456789012345678901 10]
7. paths [0123456789012345678901234567890101234567890123456789012345678901 11]
8. paths [0123456789012345678901234567890101234567890123456789012345678901 19]
9. paths [19]`
if have := b.String(); have != want {
t.Errorf("have:%v\nwant:%v\n", have, want)
}
}
{
var b = new(bytes.Buffer)
for i := 0; i < len(pathsets); i++ {
fmt.Fprintf(b, "\n%d. pathset %x", i, pathsets[i])
}
want := `
0. pathset [0099]
1. pathset [0123456789012345678901234567890101234567890123456789012345678901 00 0095 0096 0097 0099 10 11 19]
2. pathset [19]`
if have := b.String(); have != want {
t.Errorf("have:%v\nwant:%v\n", have, want)
}
}
}
Loading

0 comments on commit 8778779

Please sign in to comment.