From 1d6ad73afe67ffeef2cdaf0eb99d91b327e35e97 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Mon, 25 Sep 2023 15:46:14 -0400 Subject: [PATCH 1/9] Add initialize option to 'goal node catchup'. --- cmd/goal/node.go | 62 +++++++++++++++------------ daemon/algod/api/client/restClient.go | 8 +++- libgoal/libgoal.go | 10 ++--- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/cmd/goal/node.go b/cmd/goal/node.go index 50856b2212..e32292dff6 100644 --- a/cmd/goal/node.go +++ b/cmd/goal/node.go @@ -61,6 +61,7 @@ var newNodeFullConfig bool var watchMillisecond uint64 var abortCatchup bool var fastCatchupForce bool +var initializeCatchup uint64 const catchpointURL = "https://algorand-catchpoints.s3.us-east-2.amazonaws.com/channel/%s/latest.catchpoint" @@ -116,6 +117,7 @@ func init() { catchupCmd.Flags().BoolVarP(&abortCatchup, "abort", "x", false, "Aborts the current catchup process") catchupCmd.Flags().BoolVar(&fastCatchupForce, "force", false, "Forces fast catchup with implicit catchpoint to start without a consent prompt") + catchupCmd.Flags().Uint64VarP(&initializeCatchup, "initialize", "i", 0, "Catchup only if the catchpoint would advance the node by at least the specified number of rounds") } @@ -161,9 +163,24 @@ var catchupCmd = &cobra.Command{ Example: "goal node catchup 6500000#1234567890ABCDEF01234567890ABCDEF0\tStart catching up to round 6500000 with the provided catchpoint\ngoal node catchup --abort\t\t\t\t\tAbort the current catchup", Args: catchpointCmdArgument, Run: func(cmd *cobra.Command, args []string) { + var catchpoint string + // assume first positional parameter is the catchpoint + if len(args) != 0 { + catchpoint = args[0] + } datadir.OnDataDirs(func(dataDir string) { - if !abortCatchup && len(args) == 0 { - client := ensureAlgodClient(dataDir) + client := ensureAlgodClient(dataDir) + + if abortCatchup { + err := client.AbortCatchup() + if err != nil { + reportErrorf(errorNodeStatus, err) + } + return + } + + // lookup missing catchpoint + if catchpoint == "" { vers, err := client.AlgodVersions() if err != nil { reportErrorf(errorNodeStatus, err) @@ -174,18 +191,24 @@ var catchupCmd = &cobra.Command{ if err != nil { reportErrorf(errorCatchpointLabelMissing, errorUnableToLookupCatchpointLabel, err.Error()) } - args = append(args, label) - if !fastCatchupForce { - fmt.Printf(nodeConfirmImplicitCatchpoint, label) - reader := bufio.NewReader(os.Stdin) - text, _ := reader.ReadString('\n') - text = strings.Replace(text, "\n", "", -1) - if text != "yes" { - reportErrorf(errorAbortedPerUserRequest) - } + catchpoint = label + } + + if !fastCatchupForce { + fmt.Printf(nodeConfirmImplicitCatchpoint, catchpoint) + reader := bufio.NewReader(os.Stdin) + text, _ := reader.ReadString('\n') + text = strings.Replace(text, "\n", "", -1) + if text != "yes" { + reportErrorf(errorAbortedPerUserRequest) } } - catchup(dataDir, args) + + resp, err := client.Catchup(catchpoint, initializeCatchup) + if err != nil { + reportErrorf(errorNodeStatus, err) + } + reportInfof("node response: %s", resp.CatchupMessage) }) }, } @@ -718,21 +741,6 @@ var createCmd = &cobra.Command{ }, } -func catchup(dataDir string, args []string) { - client := ensureAlgodClient(datadir.EnsureSingleDataDir()) - if abortCatchup { - err := client.AbortCatchup() - if err != nil { - reportErrorf(errorNodeStatus, err) - } - return - } - err := client.Catchup(args[0]) - if err != nil { - reportErrorf(errorNodeStatus, err) - } -} - // verifyPeerDialArg verifies that the peers provided in peerDial are valid peers. func verifyPeerDialArg() bool { if peerDial == "" { diff --git a/daemon/algod/api/client/restClient.go b/daemon/algod/api/client/restClient.go index 0448f650c4..dbdcf8a5ef 100644 --- a/daemon/algod/api/client/restClient.go +++ b/daemon/algod/api/client/restClient.go @@ -389,6 +389,10 @@ type accountInformationParams struct { Exclude string `url:"exclude"` } +type catchupParams struct { + Initialize uint64 `url:"initialize"` +} + // PendingTransactionsByAddr returns all the pending transactions for an addr. func (client RestClient) PendingTransactionsByAddr(addr string, max uint64) (response model.PendingTransactionsResponse, err error) { err = client.get(&response, fmt.Sprintf("/v2/accounts/%s/transactions/pending", addr), pendingTransactionsByAddrParams{max}) @@ -575,8 +579,8 @@ func (client RestClient) AbortCatchup(catchpointLabel string) (response model.Ca } // Catchup start catching up to the give catchpoint label -func (client RestClient) Catchup(catchpointLabel string) (response model.CatchpointStartResponse, err error) { - err = client.submitForm(&response, fmt.Sprintf("/v2/catchup/%s", catchpointLabel), nil, nil, "POST", false, true, false) +func (client RestClient) Catchup(catchpointLabel string, initializeRounds uint64) (response model.CatchpointStartResponse, err error) { + err = client.submitForm(&response, fmt.Sprintf("/v2/catchup/%s", catchpointLabel), catchupParams{Initialize: initializeRounds}, nil, "POST", false, true, false) return } diff --git a/libgoal/libgoal.go b/libgoal/libgoal.go index 2bc924bac9..5788d64e23 100644 --- a/libgoal/libgoal.go +++ b/libgoal/libgoal.go @@ -1131,16 +1131,12 @@ func (c *Client) AbortCatchup() error { } // Catchup start catching up to the give catchpoint label. -func (c *Client) Catchup(catchpointLabel string) error { +func (c *Client) Catchup(catchpointLabel string, initialize uint64) (model.CatchpointStartResponse, error) { algod, err := c.ensureAlgodClient() if err != nil { - return err + return model.CatchpointStartResponse{}, err } - _, err = algod.Catchup(catchpointLabel) - if err != nil { - return err - } - return nil + return algod.Catchup(catchpointLabel, initialize) } const defaultAppIdx = 1380011588 From ad480a88fd0a64154e592112a6ed51256b176dc7 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 26 Sep 2023 07:29:11 -0400 Subject: [PATCH 2/9] Add new output to expect test. --- test/e2e-go/cli/goal/expect/goalExpectCommon.exp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp index 4728f445df..08f8d461b8 100644 --- a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp +++ b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp @@ -936,6 +936,7 @@ proc ::AlgorandGoal::StartCatchup { NODE_DATA_DIR CATCHPOINT } { spawn goal node catchup $CATCHPOINT -d $NODE_DATA_DIR expect { timeout { ::AlgorandGoal::Abort "goal node catchup timed out" } + -re {Fast catchup response: ([0-9]*#[A-Z2-7]*)} {regexp -- {[0-9]*#[A-Z2-7]*} $expect_out(0,string) CATCHPOINT; exp_continue } eof { catch wait result; if { [lindex $result 3] != 0 } { ::AlgorandGoal::Abort "failed to start catching up : error code [lindex $result 3]"} } } } EXCEPTION ] } { From 52f43e17e164056feb37eb22c97a66baa3e8ef29 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 26 Sep 2023 07:54:41 -0400 Subject: [PATCH 3/9] Suppress catchpoint message. --- cmd/goal/node.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/goal/node.go b/cmd/goal/node.go index e32292dff6..4abe1275f5 100644 --- a/cmd/goal/node.go +++ b/cmd/goal/node.go @@ -208,7 +208,9 @@ var catchupCmd = &cobra.Command{ if err != nil { reportErrorf(errorNodeStatus, err) } - reportInfof("node response: %s", resp.CatchupMessage) + if resp.CatchupMessage != catchpoint { + reportInfof("node response: %s", resp.CatchupMessage) + } }) }, } From d56de638986e9c782c1c39e77412b63d9fd95b3a Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 26 Sep 2023 07:54:49 -0400 Subject: [PATCH 4/9] Revert "Add new output to expect test." This reverts commit ad480a88fd0a64154e592112a6ed51256b176dc7. --- test/e2e-go/cli/goal/expect/goalExpectCommon.exp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp index 08f8d461b8..4728f445df 100644 --- a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp +++ b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp @@ -936,7 +936,6 @@ proc ::AlgorandGoal::StartCatchup { NODE_DATA_DIR CATCHPOINT } { spawn goal node catchup $CATCHPOINT -d $NODE_DATA_DIR expect { timeout { ::AlgorandGoal::Abort "goal node catchup timed out" } - -re {Fast catchup response: ([0-9]*#[A-Z2-7]*)} {regexp -- {[0-9]*#[A-Z2-7]*} $expect_out(0,string) CATCHPOINT; exp_continue } eof { catch wait result; if { [lindex $result 3] != 0 } { ::AlgorandGoal::Abort "failed to start catching up : error code [lindex $result 3]"} } } } EXCEPTION ] } { From 654c44641b78119c8075d415f6194339df7f3735 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 26 Sep 2023 08:59:21 -0400 Subject: [PATCH 5/9] How have these been passing without this... --- test/e2e-go/cli/goal/expect/goalExpectCommon.exp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp index 4728f445df..499e46f807 100644 --- a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp +++ b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp @@ -933,7 +933,7 @@ proc ::AlgorandGoal::StartCatchup { NODE_DATA_DIR CATCHPOINT } { if { [catch { # start catchup puts "[clock format [clock seconds] -format %H:%M:%S]: spawn node catchup $CATCHPOINT" - spawn goal node catchup $CATCHPOINT -d $NODE_DATA_DIR + spawn goal node catchup --force $CATCHPOINT -d $NODE_DATA_DIR expect { timeout { ::AlgorandGoal::Abort "goal node catchup timed out" } eof { catch wait result; if { [lindex $result 3] != 0 } { ::AlgorandGoal::Abort "failed to start catching up : error code [lindex $result 3]"} } From 9a1598b0c52e75f63e364cf3452505713900eb99 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 26 Sep 2023 09:20:35 -0400 Subject: [PATCH 6/9] Thats how. --- cmd/goal/node.go | 3 ++- test/e2e-go/cli/goal/expect/goalExpectCommon.exp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/goal/node.go b/cmd/goal/node.go index 4abe1275f5..5ed2e9ebe4 100644 --- a/cmd/goal/node.go +++ b/cmd/goal/node.go @@ -194,7 +194,8 @@ var catchupCmd = &cobra.Command{ catchpoint = label } - if !fastCatchupForce { + // Prompt user to confirm implicit catchpoint. + if !fastCatchupForce && len(args) == 0 { fmt.Printf(nodeConfirmImplicitCatchpoint, catchpoint) reader := bufio.NewReader(os.Stdin) text, _ := reader.ReadString('\n') diff --git a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp index 499e46f807..4728f445df 100644 --- a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp +++ b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp @@ -933,7 +933,7 @@ proc ::AlgorandGoal::StartCatchup { NODE_DATA_DIR CATCHPOINT } { if { [catch { # start catchup puts "[clock format [clock seconds] -format %H:%M:%S]: spawn node catchup $CATCHPOINT" - spawn goal node catchup --force $CATCHPOINT -d $NODE_DATA_DIR + spawn goal node catchup $CATCHPOINT -d $NODE_DATA_DIR expect { timeout { ::AlgorandGoal::Abort "goal node catchup timed out" } eof { catch wait result; if { [lindex $result 3] != 0 } { ::AlgorandGoal::Abort "failed to start catching up : error code [lindex $result 3]"} } From f644f49389928948be6b709901403902cbd117df Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 26 Sep 2023 09:43:01 -0400 Subject: [PATCH 7/9] Avoid second length check. --- cmd/goal/node.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/cmd/goal/node.go b/cmd/goal/node.go index 5ed2e9ebe4..d170461c29 100644 --- a/cmd/goal/node.go +++ b/cmd/goal/node.go @@ -187,21 +187,20 @@ var catchupCmd = &cobra.Command{ } genesis := strings.Split(vers.GenesisID, "-")[0] URL := fmt.Sprintf(catchpointURL, genesis) - label, err := getMissingCatchpointLabel(URL) + catchpoint, err := getMissingCatchpointLabel(URL) if err != nil { reportErrorf(errorCatchpointLabelMissing, errorUnableToLookupCatchpointLabel, err.Error()) } - catchpoint = label - } - // Prompt user to confirm implicit catchpoint. - if !fastCatchupForce && len(args) == 0 { - fmt.Printf(nodeConfirmImplicitCatchpoint, catchpoint) - reader := bufio.NewReader(os.Stdin) - text, _ := reader.ReadString('\n') - text = strings.Replace(text, "\n", "", -1) - if text != "yes" { - reportErrorf(errorAbortedPerUserRequest) + // Prompt user to confirm using an implicit catchpoint. + if !fastCatchupForce { + fmt.Printf(nodeConfirmImplicitCatchpoint, catchpoint) + reader := bufio.NewReader(os.Stdin) + text, _ := reader.ReadString('\n') + text = strings.Replace(text, "\n", "", -1) + if text != "yes" { + reportErrorf(errorAbortedPerUserRequest) + } } } From ae926be312129648d2815067ffd22e35ef2cbb30 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 26 Sep 2023 10:09:38 -0400 Subject: [PATCH 8/9] Fix tests. --- test/e2e-go/features/catchup/catchpointCatchup_test.go | 10 +++++----- .../e2e-go/features/catchup/stateproofsCatchup_test.go | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/e2e-go/features/catchup/catchpointCatchup_test.go b/test/e2e-go/features/catchup/catchpointCatchup_test.go index ecbecbdb87..f9f62b3d06 100644 --- a/test/e2e-go/features/catchup/catchpointCatchup_test.go +++ b/test/e2e-go/features/catchup/catchpointCatchup_test.go @@ -316,7 +316,7 @@ func TestCatchpointCatchupFailure(t *testing.T) { err = primaryNode.StopAlgod() a.NoError(err) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.ErrorContains(err, node.MakeStartCatchpointError(catchpointLabel, fmt.Errorf("")).Error()) } @@ -358,7 +358,7 @@ func TestBasicCatchpointCatchup(t *testing.T) { catchpointLabel := waitForCatchpointGeneration(t, fixture, primaryNodeRestClient, targetCatchpointRound) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.NoError(err) err = fixture.ClientWaitForRoundWithTimeout(usingNodeRestClient, uint64(targetCatchpointRound+1)) @@ -534,7 +534,7 @@ func TestNodeTxHandlerRestart(t *testing.T) { lastCatchpoint := waitForCatchpointGeneration(t, &fixture, relayClient, basics.Round(targetCatchpointRound)) // let the primary node catchup - err = client1.Catchup(lastCatchpoint) + _, err = client1.Catchup(lastCatchpoint, 0) a.NoError(err) status1, err := client1.Status() @@ -647,7 +647,7 @@ func TestReadyEndpoint(t *testing.T) { // Then when the primary node is at target round, it should satisfy ready 200 condition // let the primary node catchup - err = client1.Catchup(lastCatchpoint) + _, err = client1.Catchup(lastCatchpoint, 0) a.NoError(err) // The primary node is catching up with its previous catchpoint @@ -789,7 +789,7 @@ func TestNodeTxSyncRestart(t *testing.T) { _, err = fixture.StartNode(primaryNode.GetDataDir()) a.NoError(err) // let the primary node catchup - err = client1.Catchup(lastCatchpoint) + _, err = client1.Catchup(lastCatchpoint, 0) a.NoError(err) // the transaction should not be confirmed yet diff --git a/test/e2e-go/features/catchup/stateproofsCatchup_test.go b/test/e2e-go/features/catchup/stateproofsCatchup_test.go index 7f11ca37f5..4d3140c8d0 100644 --- a/test/e2e-go/features/catchup/stateproofsCatchup_test.go +++ b/test/e2e-go/features/catchup/stateproofsCatchup_test.go @@ -93,7 +93,7 @@ func TestStateProofInReplayCatchpoint(t *testing.T) { catchpointLabel := waitForCatchpointGeneration(t, fixture, primaryNodeRestClient, targetCatchpointRound) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.NoError(err) // waiting for fastcatchup to start @@ -169,7 +169,7 @@ func TestStateProofAfterCatchpoint(t *testing.T) { catchpointLabel := waitForCatchpointGeneration(t, fixture, primaryNodeRestClient, targetCatchpointRound) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.NoError(err) roundAfterSPGeneration := targetCatchpointRound.RoundUpToMultipleOf(basics.Round(consensusParams.StateProofInterval)) + @@ -258,7 +258,7 @@ func TestSendSigsAfterCatchpointCatchup(t *testing.T) { targetCatchpointRound := getFirstCatchpointRound(&consensusParams) catchpointLabel := waitForCatchpointGeneration(t, &fixture, primaryNodeRestClient, targetCatchpointRound) - _, err = usingNodeRestClient.Catchup(catchpointLabel) + _, err = usingNodeRestClient.Catchup(catchpointLabel, 0) a.NoError(err) err = fixture.ClientWaitForRoundWithTimeout(usingNodeRestClient, uint64(targetCatchpointRound)+1) From 5226539c35aef920c2dc499af2fafc062a1f2f75 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Tue, 26 Sep 2023 10:30:04 -0400 Subject: [PATCH 9/9] Fix reviewdog error. --- cmd/goal/node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/goal/node.go b/cmd/goal/node.go index d170461c29..11b95f7c61 100644 --- a/cmd/goal/node.go +++ b/cmd/goal/node.go @@ -187,7 +187,7 @@ var catchupCmd = &cobra.Command{ } genesis := strings.Split(vers.GenesisID, "-")[0] URL := fmt.Sprintf(catchpointURL, genesis) - catchpoint, err := getMissingCatchpointLabel(URL) + catchpoint, err = getMissingCatchpointLabel(URL) if err != nil { reportErrorf(errorCatchpointLabelMissing, errorUnableToLookupCatchpointLabel, err.Error()) }