From d60c93286c99a22f5adc15216ff197ecaff6a911 Mon Sep 17 00:00:00 2001 From: meows Date: Tue, 25 Apr 2023 08:41:50 -0700 Subject: [PATCH 01/10] Jenkinsfile: bump go version 1.18.3 to 1.20.3 The CI on Jenkins failed to build geth because minimum supported version is now 1.19. So bump to latest available Go version. Date: 2023-04-25 08:41:50-07:00 Signed-off-by: meows --- Jenkinsfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 10bf6bad3b..8e7154b00f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,8 +22,8 @@ pipeline { stage('Kotti') { agent { label "aws-slave-m5-xlarge" } steps { - sh "curl -L -O https://go.dev/dl/go1.18.3.linux-amd64.tar.gz" - sh "sudo rm -rf /usr/bin/go && sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.18.3.linux-amd64.tar.gz" + sh "curl -L -O https://go.dev/dl/go1.20.3.linux-amd64.tar.gz" + sh "sudo rm -rf /usr/bin/go && sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.3.linux-amd64.tar.gz" sh "sudo cp /usr/local/go/bin/go /usr/bin/go" sh "sudo cp /usr/local/go/bin/gofmt /usr/bin/gofmt" sh "go version" @@ -41,8 +41,8 @@ pipeline { stage('Mordor') { agent { label "aws-slave-m5-xlarge" } steps { - sh "curl -L -O https://go.dev/dl/go1.18.3.linux-amd64.tar.gz" - sh "sudo rm -rf /usr/bin/go && sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.18.3.linux-amd64.tar.gz" + sh "curl -L -O https://go.dev/dl/go1.20.3.linux-amd64.tar.gz" + sh "sudo rm -rf /usr/bin/go && sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.3.linux-amd64.tar.gz" sh "sudo cp /usr/local/go/bin/go /usr/bin/go" sh "sudo cp /usr/local/go/bin/gofmt /usr/bin/gofmt" sh "go version" @@ -61,8 +61,8 @@ pipeline { stage('Goerli') { agent { label "aws-slave-m5-xlarge" } steps { - sh "curl -L -O https://go.dev/dl/go1.18.3.linux-amd64.tar.gz" - sh "sudo rm -rf /usr/bin/go && sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.18.3.linux-amd64.tar.gz" + sh "curl -L -O https://go.dev/dl/go1.20.3.linux-amd64.tar.gz" + sh "sudo rm -rf /usr/bin/go && sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.3.linux-amd64.tar.gz" sh "sudo cp /usr/local/go/bin/go /usr/bin/go" sh "sudo cp /usr/local/go/bin/gofmt /usr/bin/gofmt" sh "go version" From 42d4dd3861625d0a9943e6164004a0e1c956e4f2 Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Mon, 15 May 2023 18:53:22 +0300 Subject: [PATCH 02/10] params: bump version from v1.12.11-unstable to v1.12.11-stable Signed-off-by: Chris Ziogas --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index f7f1d51381..8c4fa89a02 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 12 // Minor version component of the current release - VersionPatch = 11 // Patch version component of the current release - VersionMeta = "unstable" // Version metadata to append to the version string + VersionMajor = 1 // Major version component of the current release + VersionMinor = 12 // Minor version component of the current release + VersionPatch = 11 // Patch version component of the current release + VersionMeta = "stable" // Version metadata to append to the version string VersionName = "CoreGeth" ) From d6ca94fd115ad04701d4680cd25a036bdcd184ce Mon Sep 17 00:00:00 2001 From: Chris Ziogas Date: Mon, 15 May 2023 19:08:06 +0300 Subject: [PATCH 03/10] params: bump version from v1.12.11-stable to v1.12.12-unstable Signed-off-by: Chris Ziogas --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index 8c4fa89a02..5da7816a36 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 12 // Minor version component of the current release - VersionPatch = 11 // Patch version component of the current release - VersionMeta = "stable" // Version metadata to append to the version string + VersionMajor = 1 // Major version component of the current release + VersionMinor = 12 // Minor version component of the current release + VersionPatch = 12 // Patch version component of the current release + VersionMeta = "unstable" // Version metadata to append to the version string VersionName = "CoreGeth" ) From 22d8ba95c509bce873c86a3b5f137af929e60322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20L=C3=B3pez=20Leo=CC=81n?= Date: Mon, 15 May 2023 18:05:29 -0300 Subject: [PATCH 04/10] Bump GH Action versions --- .github/workflows/release-packages.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml index 27367ecc62..92580a35b7 100644 --- a/.github/workflows/release-packages.yml +++ b/.github/workflows/release-packages.yml @@ -35,12 +35,12 @@ jobs: steps: - name: Checkout etclabscore/core-geth - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Go 1.19 if: ${{ matrix.BUILD_OS_NAME != 'arm' }} id: go - uses: actions/setup-go@v2 + uses: actions/setup-go@v4 with: go-version: ^1.19 From 245322711281f594416c2b39135b17d76695bcb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20L=C3=B3pez=20Leo=CC=81n?= Date: Mon, 15 May 2023 16:41:15 -0300 Subject: [PATCH 05/10] Print Core-Geth version after build --- .github/workflows/release-packages.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml index 92580a35b7..218ac8ac1b 100644 --- a/.github/workflows/release-packages.yml +++ b/.github/workflows/release-packages.yml @@ -76,6 +76,14 @@ jobs: go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc env BUILD_OS_NAME=arm64 ./build/archive-signing.sh + - name: Print Core-Geth version (Linux/MacOS) + if: ${{ matrix.BUILD_OS_NAME == 'linux' || matrix.BUILD_OS_NAME == 'osx'}} + run: ./build/bin/geth version + + - name: Print Core-Geth version (Windows) + if: ${{ matrix.BUILD_OS_NAME == 'win64' }} + run: ./build/bin/geth.exe version + - name: Prepare archives for release if: ${{ matrix.BUILD_OS_NAME != 'arm' }} run: ./build/archive-signing.sh From f72215b680438b15f0cd6a658597f87fbc923846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20L=C3=B3pez=20Leo=CC=81n?= Date: Mon, 15 May 2023 17:13:20 -0300 Subject: [PATCH 06/10] Add GH cache --- .github/workflows/release-packages.yml | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml index 218ac8ac1b..716a96d211 100644 --- a/.github/workflows/release-packages.yml +++ b/.github/workflows/release-packages.yml @@ -44,6 +44,39 @@ jobs: with: go-version: ^1.19 + - name: Cache lookup (Linux/ARM) + uses: actions/cache@v3 + if: ${{ matrix.BUILD_OS_NAME == 'linux' || matrix.BUILD_OS_NAME == 'arm'}} + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ matrix.BUILD_OS_NAME }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ matrix.BUILD_OS_NAME }}-go- + + - name: Cache lookup (MacOS) + uses: actions/cache@v3 + if: ${{ matrix.BUILD_OS_NAME == 'osx'}} + with: + path: | + ~/Library/Caches/go-build + ~/go/pkg/mod + key: ${{ matrix.BUILD_OS_NAME }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ matrix.BUILD_OS_NAME }}-go- + + - name: Cache lookup (Windows) + uses: actions/cache@v3 + if: ${{ matrix.BUILD_OS_NAME == 'win64'}} + with: + path: | + ~\AppData\Local\go-build + ~\go\pkg\mod + key: ${{ matrix.BUILD_OS_NAME }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ matrix.BUILD_OS_NAME }}-go- + - name: Build all packages (non ARM) if: ${{ matrix.BUILD_OS_NAME != 'arm' }} id: build-non-arm-images From 5b6d627954c9e2ad8d0c22fce6caaa351133265c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20L=C3=B3pez=20Leo=CC=81n?= Date: Mon, 15 May 2023 16:16:38 -0300 Subject: [PATCH 07/10] Bump Ubuntu version to 20.04 --- .github/workflows/release-packages.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml index 716a96d211..0cd7217ca3 100644 --- a/.github/workflows/release-packages.yml +++ b/.github/workflows/release-packages.yml @@ -20,14 +20,10 @@ jobs: - os: macos-latest BUILD_OS_NAME: osx - # Build on 18.04 (Bionic) because the OS GLIBC is bumped above 2.26 in later versions (eg. 20.04). - # This GLIBC version included and used by default in at least Amazon Linux 2 and Ubuntu Bionic. - # The C compiler used by default in the 18.04 runner produces an executable which is runnable - # on Ubuntu Disco and Focal (tested by meowsbits); it is forward compatible. - - os: ubuntu-18.04 + - os: ubuntu-20.04 BUILD_OS_NAME: linux - - os: ubuntu-18.04 + - os: ubuntu-20.04 BUILD_OS_NAME: arm - os: windows-latest From b2a65e4dd018dbf5d7c25ba94affa8e68d482860 Mon Sep 17 00:00:00 2001 From: meows Date: Wed, 17 May 2023 06:29:29 -0700 Subject: [PATCH 08/10] eth/downloader,eth,eth/protocols/eth,eth: zurich's Ghost-128 countermeasure; periodic TD check This patch applies the countermeasure to security vulnerability Ghost-128 described by Taverna and Paterson of ETH Zurich in their 2023 paper Snapping Snap Sync: Practical Attacks on Synchronising Go-Ethereum Nodes. This patch, in both its design and implementation, was originally authored and provided by the authors of that paper, and details around both can be found in the paper's Section 7. The patch has been only minorly edited in adaptation to minor application conflicts. A link to the research paper: https://appliedcrypto.ethz.ch/content/dam/ethz/special-interest/infk/inst-infsec/appliedcrypto/research/TavernaPaterson-SnappingSnapSync.pdf Date: 2023-05-17 06:29:29-07:00 Signed-off-by: meows eth/downloader: implement Ghost-128.patch methods for peer test types Date: 2023-05-17 06:44:23-07:00 Signed-off-by: meows eth/downloader: goimports Date: 2023-05-17 06:45:21-07:00 Signed-off-by: meows eth: fix p.Head implementation for WorstPeer method Date: 2023-05-17 06:46:40-07:00 Signed-off-by: meows --- eth/downloader/downloader.go | 83 +++++++++++++++++++++++++++++-- eth/downloader/downloader_test.go | 10 +++- eth/downloader/peer.go | 8 ++- eth/downloader/skeleton_test.go | 8 ++- eth/handler_eth.go | 4 +- eth/peer.go | 2 +- eth/peerset.go | 4 +- eth/protocols/eth/peer.go | 17 ++++--- eth/sync.go | 2 +- 9 files changed, 115 insertions(+), 23 deletions(-) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 85a5431576..039f721352 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -58,6 +58,9 @@ var ( fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it fsHeaderContCheck = 3 * time.Second // Time interval to check for header continuations during state download fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in snap sync + + maxTotalDifficultyDistance = 10 // Maximum amount of block difficulty units the master peer can lag behind w.r.t. other peers + totalDifficultyContCheck = 13 * time.Second // Time interval to wait between total difficulty checks ) var ( @@ -129,6 +132,7 @@ type Downloader struct { // Channels headerProcCh chan *headerTask // Channel to feed the header processor new tasks + totalDiffCh chan struct{} // Channel to notify the total difficulty checker about end of header processing // Skeleton sync skeleton *skeleton // Header skeleton to backfill the chain with (eth2 mode) @@ -140,6 +144,9 @@ type Downloader struct { SnapSyncer *snap.Syncer // TODO(karalabe): make private! hack for now stateSyncStart chan *stateSync + // Sync target total difficulty + td *big.Int + // Cancellation and termination cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop) cancelCh chan struct{} // Channel to cancel mid-flight syncs @@ -233,6 +240,7 @@ func New(checkpoint uint64, stateDb ethdb.Database, mux *event.TypeMux, chain Bl lightchain: lightchain, dropPeer: dropPeer, headerProcCh: make(chan *headerTask, 1), + totalDiffCh: make(chan struct{}), quitCh: make(chan struct{}), SnapSyncer: snap.NewSyncer(stateDb, chain.TrieDB().Scheme()), stateSyncStart: make(chan *stateSync), @@ -419,6 +427,10 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td, ttd *big.Int, default: } } + select { + case <-d.totalDiffCh: + default: + } for empty := false; !empty; { select { case <-d.headerProcCh: @@ -430,6 +442,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td, ttd *big.Int, d.cancelLock.Lock() d.cancelCh = make(chan struct{}) d.cancelPeer = id + d.td = td d.cancelLock.Unlock() defer d.Cancel() // No matter what, we can't leave the cancel channel open @@ -637,7 +650,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * headerFetcher, // Headers are always retrieved func() error { return d.fetchBodies(origin+1, beaconMode) }, // Bodies are retrieved during normal and snap sync func() error { return d.fetchReceipts(origin+1, beaconMode) }, // Receipts are retrieved during snap sync - func() error { return d.processHeaders(origin+1, td, ttd, beaconMode) }, + func() error { return d.processHeaders(origin+1, ttd, beaconMode) }, } if mode == SnapSync { d.pivotLock.Lock() @@ -648,6 +661,8 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * } else if mode == FullSync { fetchers = append(fetchers, func() error { return d.processFullSyncContent(ttd, beaconMode) }) } + fetchers = append(fetchers, func() error { return d.fetchTotalDifficulty(p, latest) }) + return d.spawnSync(fetchers) } @@ -729,7 +744,8 @@ func (d *Downloader) fetchHead(p *peerConnection) (head *types.Header, pivot *ty mode := d.getMode() // Request the advertised remote head block and wait for the response - latest, _ := p.peer.Head() + latest, peerTd, _ := p.peer.Head() + d.td = peerTd fetch := 1 if mode == SnapSync { fetch = 2 // head + pivot headers @@ -1013,6 +1029,59 @@ func (d *Downloader) findAncestorBinarySearch(p *peerConnection, mode SyncMode, return start, nil } +func (d *Downloader) fetchTotalDifficulty(p *peerConnection, latest *types.Header) error { + var head *types.Header + head = latest + + for { + select { + case <-d.cancelCh: + return errCanceled + case <-d.totalDiffCh: + return nil + case <-time.After(totalDifficultyContCheck): + + // Check if the peer is lagging behind + for _, peer := range d.peers.AllPeers() { + _, peerTd, peerDifficulty := peer.peer.Head() + distance := new(big.Int).Sub(peerTd, d.td) + if distance.Sign() <= 0 || peerDifficulty.Sign() == 0 { + continue + } + + threshold := big.NewInt(int64(maxTotalDifficultyDistance)) + threshold.Mul(threshold, peerDifficulty) + if distance.Cmp(threshold) > 0 { + log.Warn("Found significantly higher total difficulty", "td", d.td, "better", peerTd) + return fmt.Errorf("%w: other peers have significantly heavier chains", errUnsyncedPeer) + } + } + + // Check for chain progress on the peer's side + headers, _, err := d.fetchHeadersByNumber(p, head.Number.Uint64()+1, 8, 0, false) + if err != nil { + return fmt.Errorf("%w: header request failed: %v", errBadPeer, err) + } + ignore := reorgProtHeaderDelay + if ignore > len(headers) { + ignore = len(headers) + } + headers = headers[:len(headers)-ignore] + newTd := new(big.Int).Set(d.td) + for _, header := range headers { + newTd.Add(newTd, header.Difficulty) + } + + if newTd.Cmp(d.td) > 0 { + head = headers[len(headers)-1] + p.peer.SetHead(head.Hash(), newTd, head.Difficulty) + log.Debug("Updating sync target total difficulty", "old", d.td, "new", newTd) + d.td = newTd + } + } + } +} + // fetchHeaders keeps retrieving headers concurrently from the number // requested, until no more are returned, potentially throttling on the way. To // facilitate concurrency but still protect against malicious nodes sending bad @@ -1284,7 +1353,7 @@ func (d *Downloader) fetchReceipts(from uint64, beaconMode bool) error { // processHeaders takes batches of retrieved headers from an input channel and // keeps processing and scheduling them into the header chain and downloader's // queue until the stream ends or a failure occurs. -func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode bool) error { +func (d *Downloader) processHeaders(origin uint64, ttd *big.Int, beaconMode bool) error { // Keep a count of uncertain headers to roll back var ( rollback uint64 // Zero means no rollback (fine as you can't unroll the genesis) @@ -1332,6 +1401,10 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode case <-d.cancelCh: } } + select { + case d.totalDiffCh <- struct{}{}: + case <-d.cancelCh: + } // If we're in legacy sync mode, we need to check total difficulty // violations from malicious peers. That is not needed in beacon // mode and we can skip to terminating sync. @@ -1350,7 +1423,7 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode // R: Nothing to give if mode != LightSync { head := d.blockchain.CurrentBlock() - if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 { + if !gotHeaders && d.td.Cmp(d.blockchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 { return errStallingPeer } } @@ -1363,7 +1436,7 @@ func (d *Downloader) processHeaders(origin uint64, td, ttd *big.Int, beaconMode // peer gave us something useful, we're already happy/progressed (above check). if mode == SnapSync || mode == LightSync { head := d.lightchain.CurrentHeader() - if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 { + if d.td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 { return errStallingPeer } } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index e22ee2f967..21567a0c9c 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -158,9 +158,15 @@ type downloadTesterPeer struct { // Head constructs a function to retrieve a peer's current head hash // and total difficulty. -func (dlp *downloadTesterPeer) Head() (common.Hash, *big.Int) { +func (dlp *downloadTesterPeer) Head() (common.Hash, *big.Int, *big.Int) { head := dlp.chain.CurrentBlock() - return head.Hash(), dlp.chain.GetTd(head.Hash(), head.Number.Uint64()) + return head.Hash(), dlp.chain.GetTd(head.Hash(), head.Number.Uint64()), new(big.Int).Set(dlp.chain.CurrentBlock().Difficulty) +} + +// SetHead constructs a function to retrieve a peer's current head hash +// and total difficulty. +func (dlp *downloadTesterPeer) SetHead(common.Hash, *big.Int, *big.Int) { + // noop } func unmarshalRlpHeaders(rlpdata []rlp.RawValue) []*types.Header { diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go index 6b82694959..5aca079862 100644 --- a/eth/downloader/peer.go +++ b/eth/downloader/peer.go @@ -57,7 +57,8 @@ type peerConnection struct { // LightPeer encapsulates the methods required to synchronise with a remote light peer. type LightPeer interface { - Head() (common.Hash, *big.Int) + Head() (common.Hash, *big.Int, *big.Int) + SetHead(common.Hash, *big.Int, *big.Int) RequestHeadersByHash(common.Hash, int, int, bool, chan *eth.Response) (*eth.Request, error) RequestHeadersByNumber(uint64, int, int, bool, chan *eth.Response) (*eth.Request, error) } @@ -74,7 +75,10 @@ type lightPeerWrapper struct { peer LightPeer } -func (w *lightPeerWrapper) Head() (common.Hash, *big.Int) { return w.peer.Head() } +func (w *lightPeerWrapper) Head() (common.Hash, *big.Int, *big.Int) { return w.peer.Head() } +func (w *lightPeerWrapper) SetHead(head common.Hash, td *big.Int, blockDifficulty *big.Int) { + w.peer.SetHead(head, td, blockDifficulty) +} func (w *lightPeerWrapper) RequestHeadersByHash(h common.Hash, amount int, skip int, reverse bool, sink chan *eth.Response) (*eth.Request, error) { return w.peer.RequestHeadersByHash(h, amount, skip, reverse, sink) } diff --git a/eth/downloader/skeleton_test.go b/eth/downloader/skeleton_test.go index b19494a7b0..746e4cd996 100644 --- a/eth/downloader/skeleton_test.go +++ b/eth/downloader/skeleton_test.go @@ -188,7 +188,11 @@ func (p *skeletonTestPeer) RequestHeadersByNumber(origin uint64, amount int, ski return req, nil } -func (p *skeletonTestPeer) Head() (common.Hash, *big.Int) { +func (p *skeletonTestPeer) Head() (common.Hash, *big.Int, *big.Int) { + panic("skeleton sync must not request the remote head") +} + +func (p *skeletonTestPeer) SetHead(common.Hash, *big.Int, *big.Int) { panic("skeleton sync must not request the remote head") } @@ -514,7 +518,7 @@ func TestSkeletonSyncExtend(t *testing.T) { // Tests that the skeleton sync correctly retrieves headers from one or more // peers without duplicates or other strange side effects. func TestSkeletonSyncRetrievals(t *testing.T) { - //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) // Since skeleton headers don't need to be meaningful, beyond a parent hash // progression, create a long fake chain to test with. diff --git a/eth/handler_eth.go b/eth/handler_eth.go index 4ed6335769..5597cc8686 100644 --- a/eth/handler_eth.go +++ b/eth/handler_eth.go @@ -133,8 +133,8 @@ func (h *ethHandler) handleBlockBroadcast(peer *eth.Peer, block *types.Block, td trueTD = new(big.Int).Sub(td, block.Difficulty()) ) // Update the peer's total difficulty if better than the previous - if _, td := peer.Head(); trueTD.Cmp(td) > 0 { - peer.SetHead(trueHead, trueTD) + if _, td, _ := peer.Head(); trueTD.Cmp(td) > 0 { + peer.SetHead(trueHead, trueTD, block.Difficulty()) h.chainSync.handlePeerEvent(peer) } return nil diff --git a/eth/peer.go b/eth/peer.go index aaa4d291b8..29d7a410e6 100644 --- a/eth/peer.go +++ b/eth/peer.go @@ -57,7 +57,7 @@ type ethPeer struct { // info gathers and returns some `eth` protocol metadata known about a peer. func (p *ethPeer) info() *ethPeerInfo { - hash, td := p.Head() + hash, td, _ := p.Head() info := ðPeerInfo{ Version: p.Version(), diff --git a/eth/peerset.go b/eth/peerset.go index b99b4bad2f..19bf4027fd 100644 --- a/eth/peerset.go +++ b/eth/peerset.go @@ -240,7 +240,7 @@ func (ps *peerSet) peerWithHighestTD() *eth.Peer { bestTd *big.Int ) for _, p := range ps.peers { - if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 { + if _, td, _ := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 { bestPeer, bestTd = p.Peer, td } } @@ -268,7 +268,7 @@ func (ps *peerSet) WorstPeer() *ethPeer { worstTD *big.Int ) for _, p := range ps.peers { - if _, td := p.Head(); worstPeer == nil || td.Cmp(worstTD) < 0 { + if _, td, _ := p.Head(); worstPeer == nil || td.Cmp(worstTD) < 0 { worstPeer, worstTD = p, td } } diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go index 63f35973bb..85b0158a63 100644 --- a/eth/protocols/eth/peer.go +++ b/eth/protocols/eth/peer.go @@ -73,9 +73,10 @@ type Peer struct { rw p2p.MsgReadWriter // Input/output streams for snap version uint // Protocol version negotiated - head common.Hash // Latest advertised head block hash - td *big.Int // Latest advertised head block total difficulty - forkid forkid.ID // Advertised forkid at time of handshake + head common.Hash // Latest advertised head block hash + td *big.Int // Latest advertised head block total difficulty + forkid forkid.ID // Advertised forkid at time of handshake + blockDifficulty *big.Int // Latest advertised head block difficulty knownBlocks *knownCache // Set of block hashes known to be known by this peer queuedBlocks chan *blockPropagation // Queue of blocks to broadcast to the peer @@ -113,6 +114,7 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe resDispatch: make(chan *response), txpool: txpool, term: make(chan struct{}), + blockDifficulty: big.NewInt(0), } // Start up all the broadcasters go peer.broadcastBlocks() @@ -141,21 +143,24 @@ func (p *Peer) Version() uint { } // Head retrieves the current head hash and total difficulty of the peer. -func (p *Peer) Head() (hash common.Hash, td *big.Int) { +func (p *Peer) Head() (hash common.Hash, td, blockDifficulty *big.Int) { p.lock.RLock() defer p.lock.RUnlock() copy(hash[:], p.head[:]) - return hash, new(big.Int).Set(p.td) + return hash, new(big.Int).Set(p.td), new(big.Int).Set(p.blockDifficulty) } // SetHead updates the head hash and total difficulty of the peer. -func (p *Peer) SetHead(hash common.Hash, td *big.Int) { +func (p *Peer) SetHead(hash common.Hash, td, blockDifficulty *big.Int) { p.lock.Lock() defer p.lock.Unlock() copy(p.head[:], hash[:]) p.td.Set(td) + p.blockDifficulty.Set(blockDifficulty) + //TODO: Remove following line + p.Peer.Log().Info("Updating head", "peer", p.ID(), "td", td) } // ForkID retrieves the reported forkid at the time of handshake. diff --git a/eth/sync.go b/eth/sync.go index 13d0ec2898..6d7153b455 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -258,7 +258,7 @@ func (cs *chainSyncer) nextSyncOp() *chainSyncOp { } func peerToSyncOp(mode downloader.SyncMode, p *eth.Peer) *chainSyncOp { - peerHead, peerTD := p.Head() + peerHead, peerTD, _ := p.Head() return &chainSyncOp{mode: mode, peer: p, td: peerTD, head: peerHead} } From e29e74f1dcb3f540cc9db5538782f29cc6e00037 Mon Sep 17 00:00:00 2001 From: meows Date: Wed, 17 May 2023 07:27:56 -0700 Subject: [PATCH 09/10] eth/downloader: fix downloadTesterPeer to use fake TD .*Starvation.* tests were failing because errStallingPeer was expected but no error received. The TD is advertised and assigned to the downloader as expected, but the handshake with the peer's Head method was causing the reassignment of the downloader.td field to the actual (honest) TD value by virtue of the virtuous dlp.Head method, which returned the actual chain TD. This caused the tests to fail because the sync TD was not the fake one. So this patch provides a field in the tester peer type to actually handle lying about TD, which then make the errStallingPeer error get returned. Date: 2023-05-17 07:27:56-07:00 Signed-off-by: meows --- eth/downloader/downloader_test.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index 21567a0c9c..70c41d791d 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -154,13 +154,18 @@ type downloadTesterPeer struct { chain *core.BlockChain withholdHeaders map[common.Hash]struct{} + fakeTD *big.Int } // Head constructs a function to retrieve a peer's current head hash // and total difficulty. func (dlp *downloadTesterPeer) Head() (common.Hash, *big.Int, *big.Int) { head := dlp.chain.CurrentBlock() - return head.Hash(), dlp.chain.GetTd(head.Hash(), head.Number.Uint64()), new(big.Int).Set(dlp.chain.CurrentBlock().Difficulty) + td := dlp.chain.GetTd(head.Hash(), head.Number.Uint64()) + if dlp.fakeTD != nil { + td.Set(dlp.fakeTD) + } + return head.Hash(), td, new(big.Int).Set(dlp.chain.CurrentBlock().Difficulty) } // SetHead constructs a function to retrieve a peer's current head hash @@ -992,8 +997,10 @@ func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) { defer tester.terminate() chain := testChainBase.shorten(1) - tester.newPeer("attack", protocol, chain.blocks[1:]) - if err := tester.sync("attack", big.NewInt(1000000), mode); err != errStallingPeer { + dlp := tester.newPeer("attack", protocol, chain.blocks[1:]) + fakeTD := big.NewInt(1000000) + dlp.fakeTD = fakeTD + if err := tester.sync("attack", fakeTD, mode); err != errStallingPeer { t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errStallingPeer) } } From 2ad5d20bd01d0e3ab050bdf4af78a48c7af2c68d Mon Sep 17 00:00:00 2001 From: meows Date: Thu, 18 May 2023 05:59:02 -0700 Subject: [PATCH 10/10] eth/protocols/eth: remove log at eth/peer.SetHead This gets called often for all peers, resulting in blabby logging. Peer information is available on request through the API. Date: 2023-05-18 05:59:02-07:00 Signed-off-by: meows --- eth/protocols/eth/peer.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go index 85b0158a63..16172314e1 100644 --- a/eth/protocols/eth/peer.go +++ b/eth/protocols/eth/peer.go @@ -159,8 +159,6 @@ func (p *Peer) SetHead(hash common.Hash, td, blockDifficulty *big.Int) { copy(p.head[:], hash[:]) p.td.Set(td) p.blockDifficulty.Set(blockDifficulty) - //TODO: Remove following line - p.Peer.Log().Info("Updating head", "peer", p.ID(), "td", td) } // ForkID retrieves the reported forkid at the time of handshake.