summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Lee <64482439+algojohnlee@users.noreply.github.com>2023-04-11 11:17:20 -0400
committerGitHub <noreply@github.com>2023-04-11 11:17:20 -0400
commit747553df032ee8af2638eaa3c9af37efb458ddaa (patch)
treea33643031325ca5827c76662dab2b79749a5a8c2
parent0192a92e79a7b1c3d34349d4705b36d2231589aa (diff)
parentc691c46259dd8df36ea10e092db54ee7ac0a37eb (diff)
Merge pull request #5275 from onetechnical/relbeta3.15.1v3.15.1-beta
-rw-r--r--buildnumber.dat2
-rw-r--r--ledger/ledger.go5
-rw-r--r--ledger/tracker.go8
-rw-r--r--node/follower_node.go6
-rw-r--r--node/follower_node_test.go95
-rw-r--r--test/e2e-go/features/followerNode/syncRestart_test.go126
-rw-r--r--test/testdata/nettemplates/TwoNodesFollower100SecondMaxAccountLookback2.json28
7 files changed, 261 insertions, 9 deletions
diff --git a/buildnumber.dat b/buildnumber.dat
index 573541ac9..d00491fd7 100644
--- a/buildnumber.dat
+++ b/buildnumber.dat
@@ -1 +1 @@
-0
+1
diff --git a/ledger/ledger.go b/ledger/ledger.go
index 50222d2f9..21547e30b 100644
--- a/ledger/ledger.go
+++ b/ledger/ledger.go
@@ -837,6 +837,11 @@ func (l *Ledger) Validate(ctx context.Context, blk bookkeeping.Block, executionP
return &vb, nil
}
+// LatestTrackerCommitted returns the trackers' dbRound which "is always exactly accountsRound()"
+func (l *Ledger) LatestTrackerCommitted() basics.Round {
+ return l.trackers.getDbRound()
+}
+
// DebuggerLedger defines the minimal set of method required for creating a debug balances.
type DebuggerLedger = internal.LedgerForCowBase
diff --git a/ledger/tracker.go b/ledger/tracker.go
index 26f7f25b2..5be0acd29 100644
--- a/ledger/tracker.go
+++ b/ledger/tracker.go
@@ -728,3 +728,11 @@ func (tr *trackerRegistry) replay(l ledgerForTracker) (err error) {
}
return
}
+
+// getDbRound accesses dbRound with protection by the trackerRegistry's mutex.
+func (tr *trackerRegistry) getDbRound() basics.Round {
+ tr.mu.RLock()
+ dbRound := tr.dbRound
+ tr.mu.RUnlock()
+ return dbRound
+}
diff --git a/node/follower_node.go b/node/follower_node.go
index 34dc48c24..ac8cad28e 100644
--- a/node/follower_node.go
+++ b/node/follower_node.go
@@ -135,10 +135,10 @@ func MakeFollower(log logging.Logger, rootDir string, cfg config.Local, phoneboo
node.catchupBlockAuth = blockAuthenticatorImpl{Ledger: node.ledger, AsyncVoteVerifier: agreement.MakeAsyncVoteVerifier(node.lowPriorityCryptoVerificationPool)}
node.catchupService = catchup.MakeService(node.log, node.config, p2pNode, node.ledger, node.catchupBlockAuth, make(chan catchup.PendingUnmatchedCertificate), node.lowPriorityCryptoVerificationPool)
- // Initialize sync round to the next round so that nothing falls out of the cache on Start
- err = node.SetSyncRound(uint64(node.Ledger().NextRound()))
+ // Initialize sync round to the latest db round + 1 so that nothing falls out of the cache on Start
+ err = node.SetSyncRound(uint64(node.Ledger().LatestTrackerCommitted() + 1))
if err != nil {
- log.Errorf("unable to set sync round to Ledger.NextRound %v", err)
+ log.Errorf("unable to set sync round to Ledger.DBRound %v", err)
return nil, err
}
diff --git a/node/follower_node_test.go b/node/follower_node_test.go
index 4389ed3e0..33a004acd 100644
--- a/node/follower_node_test.go
+++ b/node/follower_node_test.go
@@ -48,6 +48,12 @@ func followNodeDefaultGenesis() bookkeeping.Genesis {
MicroAlgos: basics.MicroAlgos{Raw: 1000000000},
},
},
+ {
+ Address: sinkAddr.String(),
+ State: basics.AccountData{
+ MicroAlgos: basics.MicroAlgos{Raw: 1000000},
+ },
+ },
},
}
}
@@ -61,6 +67,20 @@ func setupFollowNode(t *testing.T) *AlgorandFollowerNode {
return node
}
+func remakeableFollowNode(t *testing.T, tempDir string, maxAcctLookback uint64) (*AlgorandFollowerNode, string) {
+ cfg := config.GetDefaultLocal()
+ cfg.EnableFollowMode = true
+ cfg.DisableNetworking = true
+ cfg.MaxAcctLookback = maxAcctLookback
+ genesis := followNodeDefaultGenesis()
+ if tempDir == "" {
+ tempDir = t.TempDir()
+ }
+ followNode, err := MakeFollower(logging.Base(), tempDir, cfg, []string{}, genesis)
+ require.NoError(t, err)
+ return followNode, tempDir
+}
+
func TestSyncRound(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
@@ -73,13 +93,13 @@ func TestSyncRound(t *testing.T) {
b.CurrentProtocol = protocol.ConsensusCurrentVersion
err := node.Ledger().AddBlock(b, agreement.Certificate{})
require.NoError(t, err)
- latestRound := uint64(node.Ledger().Latest())
- // Sync Round should be initialized to the ledger's latest round
- require.Equal(t, latestRound, node.GetSyncRound())
+ dbRound := uint64(node.Ledger().LatestTrackerCommitted())
+ // Sync Round should be initialized to the ledger's dbRound + 1
+ require.Equal(t, dbRound+1, node.GetSyncRound())
// Set a new sync round
- require.NoError(t, node.SetSyncRound(latestRound+10))
+ require.NoError(t, node.SetSyncRound(dbRound+11))
// Ensure it is persisted
- require.Equal(t, latestRound+10, node.GetSyncRound())
+ require.Equal(t, dbRound+11, node.GetSyncRound())
// Unset the sync round and make sure get returns 0
node.UnsetSyncRound()
require.Equal(t, uint64(0), node.GetSyncRound())
@@ -127,3 +147,68 @@ func TestDevModeWarning(t *testing.T) {
require.NotNil(t, foundEntry)
require.Contains(t, foundEntry.Message, "Follower running on a devMode network. Must submit txns to a different node.")
}
+
+// TestSyncRoundWithRemake extends TestSyncRound to simulate starting and stopping the network
+func TestSyncRoundWithRemake(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ maxAcctLookback := uint64(100)
+
+ followNode, tempDir := remakeableFollowNode(t, "", maxAcctLookback)
+ addBlock := func(round basics.Round) {
+ b := bookkeeping.Block{
+ BlockHeader: bookkeeping.BlockHeader{
+ GenesisHash: followNode.ledger.GenesisHash(),
+ Round: round,
+ RewardsState: bookkeeping.RewardsState{
+ RewardsRate: 0,
+ RewardsPool: poolAddr,
+ FeeSink: sinkAddr,
+ },
+ },
+ }
+ b.CurrentProtocol = protocol.ConsensusCurrentVersion
+ err := followNode.Ledger().AddBlock(b, agreement.Certificate{})
+ require.NoError(t, err)
+
+ status, err := followNode.Status()
+ require.NoError(t, err)
+ require.Equal(t, round, status.LastRound)
+ }
+
+ // Part I. redo TestSyncRound
+ // main differences are:
+ // * cfg.DisableNetworking = true
+ // * cfg.MaxAcctLookback = 100 (instead of 4)
+
+ addBlock(basics.Round(1))
+
+ dbRound := uint64(followNode.Ledger().LatestTrackerCommitted())
+ // Sync Round should be initialized to the ledger's dbRound + 1
+ require.Equal(t, dbRound+1, followNode.GetSyncRound())
+ // Set a new sync round
+ require.NoError(t, followNode.SetSyncRound(dbRound+11))
+ // Ensure it is persisted
+ require.Equal(t, dbRound+11, followNode.GetSyncRound())
+ // Unset the sync round and make sure get returns 0
+ followNode.UnsetSyncRound()
+ require.Equal(t, uint64(0), followNode.GetSyncRound())
+
+ // Part II. fast forward and then remake the node
+
+ newRound := basics.Round(2 * maxAcctLookback)
+ for i := basics.Round(2); i <= newRound; i++ {
+ addBlock(i)
+ }
+
+ followNode, _ = remakeableFollowNode(t, tempDir, maxAcctLookback)
+ status, err := followNode.Status()
+ require.NoError(t, err)
+ require.Equal(t, newRound, status.LastRound)
+
+ // syncRound should be at
+ // newRound - maxAcctLookback + 1 = maxAcctLookback + 1
+ syncRound := followNode.GetSyncRound()
+ require.Equal(t, uint64(maxAcctLookback+1), syncRound)
+}
diff --git a/test/e2e-go/features/followerNode/syncRestart_test.go b/test/e2e-go/features/followerNode/syncRestart_test.go
new file mode 100644
index 000000000..8137f9e24
--- /dev/null
+++ b/test/e2e-go/features/followerNode/syncRestart_test.go
@@ -0,0 +1,126 @@
+// Copyright (C) 2019-2023 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand 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 Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.
+
+package followerNode
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/algorand/go-algorand/config"
+ "github.com/algorand/go-algorand/daemon/algod/api/client"
+ "github.com/algorand/go-algorand/test/framework/fixtures"
+ "github.com/algorand/go-algorand/test/partitiontest"
+)
+
+// Overview of this test:
+// Start a two-node network--one in follower mode (follower has 0%, secondary has 100%)
+// with the nodes having a max account lookback of 2.
+// Advance the primary node to particular rounds, set the follower's sync round
+// and then advance the follower node as much as possible.
+// Restart the network and verify that the sync round hasn't advanced.
+//
+// NOTE: with a max account lookback of MAL, and the follower's sync round at SR:
+// the follower cannot advance past round SR - 1 + MAL
+func TestSyncRestart(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ defer fixtures.ShutdownSynchronizedTest(t)
+
+ if testing.Short() {
+ t.Skip()
+ }
+ t.Parallel()
+ a := require.New(fixtures.SynchronizedTest(t))
+
+ var fixture fixtures.RestClientFixture
+ fixture.Setup(t, filepath.Join("nettemplates", "TwoNodesFollower100SecondMaxAccountLookback2.json"))
+
+ defer fixture.Shutdown()
+
+ // sanity check that the follower has the expected max account lookback of 2:
+ followerCtrl, err := fixture.GetNodeController("Follower")
+ a.NoError(err)
+ cfg, err := config.LoadConfigFromDisk(followerCtrl.GetDataDir())
+ a.NoError(err)
+ a.Equal(uint64(2), cfg.MaxAcctLookback)
+
+ waitTill := func(node string, round uint64) {
+ controller, err := fixture.GetNodeController(node)
+ a.NoError(err)
+ err = fixture.ClientWaitForRoundWithTimeout(fixture.GetAlgodClientForController(controller), round)
+ a.NoError(err)
+ }
+
+ getAlgod := func(node string) client.RestClient {
+ controller, err := fixture.GetNodeController(node)
+ a.NoError(err)
+ algod := fixture.GetAlgodClientForController(controller)
+ return algod
+ }
+
+ getRound := func(node string) uint64 {
+ algod := getAlgod(node)
+ status, err := algod.Status()
+ a.NoError(err)
+ return status.LastRound
+ }
+
+ getSyncRound := func() uint64 {
+ followClient := getAlgod("Follower")
+ rResp, err := followClient.GetSyncRound()
+ a.NoError(err)
+ return rResp.Round
+ }
+
+ a.Equal(uint64(1), getSyncRound())
+
+ waitTill("Primary", 3)
+ // with a max account lookback of 2, and the sync round at 1,
+ // the follower cannot advance past round 2 = 1 - 1 + 2
+ waitTill("Follower", 2)
+ a.LessOrEqual(uint64(3), getRound("Primary"))
+ a.Equal(uint64(2), getRound("Follower"))
+ a.Equal(uint64(1), getSyncRound())
+
+ /** restart the network **/
+ fixture.ShutdownImpl(true)
+ fixture.Start()
+
+ a.LessOrEqual(uint64(3), getRound("Primary"))
+ a.Equal(uint64(1), getSyncRound())
+ a.Equal(uint64(2), getRound("Follower"))
+
+ waitTill("Primary", 6)
+ followerClient := getAlgod("Follower")
+ err = followerClient.SetSyncRound(uint64(3))
+ a.NoError(err)
+ a.Equal(uint64(3), getSyncRound())
+ // with a max account lookback of 2, and the sync round at 3,
+ // the follower cannot advance past round 4 = 3 - 1 + 2
+ waitTill("Follower", 4)
+ a.LessOrEqual(uint64(6), getRound("Primary"))
+ a.Equal(uint64(4), getRound("Follower"))
+ a.Equal(uint64(3), getSyncRound())
+
+ fixture.ShutdownImpl(true)
+ fixture.Start()
+
+ a.LessOrEqual(uint64(6), getRound("Primary"))
+ a.Equal(uint64(4), getRound("Follower"))
+ a.Equal(uint64(3), getSyncRound())
+}
diff --git a/test/testdata/nettemplates/TwoNodesFollower100SecondMaxAccountLookback2.json b/test/testdata/nettemplates/TwoNodesFollower100SecondMaxAccountLookback2.json
new file mode 100644
index 000000000..bae2201d1
--- /dev/null
+++ b/test/testdata/nettemplates/TwoNodesFollower100SecondMaxAccountLookback2.json
@@ -0,0 +1,28 @@
+{
+ "Genesis": {
+ "LastPartKeyRound": 3000,
+ "NetworkName": "tbd",
+ "Wallets": [
+ {
+ "Name": "Wallet1",
+ "Stake": 100,
+ "Online": true
+ }
+ ]
+ },
+ "Nodes": [
+ {
+ "Name": "Follower",
+ "Wallets": [],
+ "ConfigJSONOverride": "{\"EnableFollowMode\":true, \"MaxAcctLookback\":2}"
+ },
+ {
+ "Name": "Primary",
+ "IsRelay": true,
+ "Wallets": [
+ { "Name": "Wallet1",
+ "ParticipationOnly": false }
+ ]
+ }
+ ]
+}