summaryrefslogtreecommitdiff
path: root/test/e2e-go/features/participation/participationExpiration_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'test/e2e-go/features/participation/participationExpiration_test.go')
-rw-r--r--test/e2e-go/features/participation/participationExpiration_test.go196
1 files changed, 196 insertions, 0 deletions
diff --git a/test/e2e-go/features/participation/participationExpiration_test.go b/test/e2e-go/features/participation/participationExpiration_test.go
new file mode 100644
index 000000000..915691d03
--- /dev/null
+++ b/test/e2e-go/features/participation/participationExpiration_test.go
@@ -0,0 +1,196 @@
+// Copyright (C) 2019-2021 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 participation
+
+import (
+ "path/filepath"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/logging"
+ "github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/test/framework/fixtures"
+ "github.com/algorand/go-algorand/test/partitiontest"
+)
+
+func testExpirationAccounts(t *testing.T, fixture *fixtures.RestClientFixture, finalStatus basics.Status, protocolCheck string) {
+
+ a := require.New(fixtures.SynchronizedTest(t))
+ pClient := fixture.GetLibGoalClientForNamedNode("Primary")
+
+ sClient := fixture.GetLibGoalClientForNamedNode("Secondary")
+ sWH, err := sClient.GetUnencryptedWalletHandle()
+ a.NoError(err)
+ sAccount, err := sClient.GenerateAddress(sWH)
+ a.NoError(err)
+
+ // send money to new account from some other account in the template, so that account can go online
+ accountList, err := fixture.GetWalletsSortedByBalance()
+ a.NoError(err)
+ richAccount := accountList[0].Address
+ _, initialRound := fixture.GetBalanceAndRound(richAccount)
+
+ minTxnFee, minAcctBalance, err := fixture.MinFeeAndBalance(initialRound)
+ a.NoError(err)
+
+ transactionFee := minTxnFee
+ amountToSendInitial := 5 * minAcctBalance
+
+ initialAmt, err := sClient.GetBalance(sAccount)
+ a.NoError(err)
+
+ fixture.SendMoneyAndWait(initialRound, amountToSendInitial, transactionFee, richAccount, sAccount, "")
+
+ newAmt, err := sClient.GetBalance(sAccount)
+ a.NoError(err)
+
+ a.GreaterOrEqual(newAmt, initialAmt)
+
+ newAccountStatus, err := pClient.AccountInformation(sAccount)
+ a.NoError(err)
+ a.Equal(basics.Offline.String(), newAccountStatus.Status)
+
+ var onlineTxID string
+ var partKeyLastValid uint64
+
+ startTime := time.Now()
+ for time.Since(startTime) < 2*time.Minute {
+ _, currentRound := fixture.GetBalanceAndRound(richAccount)
+ // account adds part key
+ partKeyFirstValid := uint64(0)
+ partKeyValidityPeriod := uint64(10)
+ partKeyLastValid = currentRound + partKeyValidityPeriod
+ partkeyResponse, _, err := sClient.GenParticipationKeys(sAccount, partKeyFirstValid, partKeyLastValid, 0)
+ a.NoError(err)
+ a.Equal(sAccount, partkeyResponse.Parent.String())
+
+ // account uses part key to go online
+ goOnlineTx, err := sClient.MakeUnsignedGoOnlineTx(sAccount, &partkeyResponse, 0, 0, transactionFee, [32]byte{})
+ a.NoError(err)
+
+ a.Equal(sAccount, goOnlineTx.Src().String())
+ onlineTxID, err = sClient.SignAndBroadcastTransaction(sWH, nil, goOnlineTx)
+
+ if err == nil {
+ break
+ }
+
+ if strings.Contains(err.Error(), "transaction tries to mark an account as online with last voting round in the past") {
+ continue
+ }
+
+ // Error occurred
+ logging.TestingLog(t).Errorf("signAndBroadcastTransaction error: %s", err.Error())
+ logging.TestingLog(t).Errorf("first valid: %d, last valid: %d, current round: %d", partKeyFirstValid, partKeyLastValid, currentRound)
+ a.NoError(err)
+ }
+
+ fixture.AssertValidTxid(onlineTxID)
+ maxRoundsToWaitForTxnConfirm := uint64(3)
+
+ sNodeStatus, err := sClient.Status()
+ a.NoError(err)
+ seededRound := sNodeStatus.LastRound
+
+ fixture.WaitForTxnConfirmation(seededRound+maxRoundsToWaitForTxnConfirm, sAccount, onlineTxID)
+ sNodeStatus, _ = sClient.Status()
+ newAccountStatus, err = pClient.AccountInformation(sAccount)
+ a.NoError(err)
+ a.Equal(basics.Online.String(), newAccountStatus.Status)
+ sAccountData, err := sClient.AccountData(sAccount)
+ a.NoError(err)
+
+ lastValidRound := sAccountData.VoteLastValid
+
+ a.Equal(basics.Round(partKeyLastValid), lastValidRound)
+
+ // We want to wait until we get to one round past the last valid round
+ err = fixture.WaitForRoundWithTimeout(uint64(lastValidRound) + 1)
+ newAccountStatus, err = pClient.AccountInformation(sAccount)
+ a.NoError(err)
+
+ // The account should be online still...
+ a.Equal(basics.Online.String(), newAccountStatus.Status)
+
+ // Now we want to send a transaction to the account and test that
+ // it was taken offline after we sent it something
+
+ _, initialRound = fixture.GetBalanceAndRound(richAccount)
+
+ blk, err := sClient.Block(initialRound)
+ a.NoError(err)
+ a.Equal(blk.CurrentProtocol, protocolCheck)
+
+ fixture.SendMoneyAndWait(initialRound, amountToSendInitial, transactionFee, richAccount, sAccount, "")
+
+ err = fixture.WaitForRoundWithTimeout(uint64(initialRound) + 3)
+
+ newAccountStatus, err = pClient.AccountInformation(sAccount)
+ a.NoError(err)
+
+ // The account should be equal to the target status now
+ a.Equal(finalStatus.String(), newAccountStatus.Status)
+}
+
+// TestParticipationAccountsExpirationFuture tests that sending a transaction to an account with
+// its last valid round being less than the current round will turn it offline. This test will only
+// work when the consensus protocol enables it (in this case the future protocol)
+func TestParticipationAccountsExpirationFuture(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ if testing.Short() {
+ t.Skip()
+ }
+
+ t.Parallel()
+
+ var fixture fixtures.RestClientFixture
+
+ fixture.SetupNoStart(t, filepath.Join("nettemplates", "TwoNodesExpiredOfflineVFuture.json"))
+
+ fixture.Start()
+ defer fixture.Shutdown()
+
+ testExpirationAccounts(t, &fixture, basics.Offline, "future")
+}
+
+// TestParticipationAccountsExpirationNonFuture tests that sending a transaction to an account with
+// its last valid round being less than the current round will NOT turn it offline. This tests that
+// when the consensus protocol is less than the required version, it will not turn nodes offline
+func TestParticipationAccountsExpirationNonFuture(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ if testing.Short() {
+ t.Skip()
+ }
+
+ t.Parallel()
+
+ var fixture fixtures.RestClientFixture
+
+ // V29 is the version before participation key expiration checking was enabled
+ fixture.SetupNoStart(t, filepath.Join("nettemplates", "TwoNodesExpiredOfflineV29.json"))
+
+ fixture.Start()
+ defer fixture.Shutdown()
+
+ testExpirationAccounts(t, &fixture, basics.Online, string(protocol.ConsensusV29))
+}