diff --git a/config/consensus.go b/config/consensus.go index 0eca1e9f07..84a5c21b8c 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -391,6 +391,10 @@ type ConsensusParams struct { // release resources allocated for creating state proofs. StateProofMaxRecoveryIntervals uint64 + // StateProofExcludeTotalWeightWithRewards specifies whether to subtract rewards from excluded online accounts along with + // their account balances. + StateProofExcludeTotalWeightWithRewards bool + // EnableAssetCloseAmount adds an extra field to the ApplyData. The field contains the amount of the remaining // asset that were sent to the close-to address. EnableAssetCloseAmount bool @@ -1191,14 +1195,22 @@ func initConsensusProtocols() { Consensus[protocol.ConsensusV34] = v34 - // v33 can be upgraded to v34, with an update delay of 12h: + v35 := v34 + v35.StateProofExcludeTotalWeightWithRewards = true + + v35.ApprovedUpgrades = map[protocol.ConsensusVersion]uint64{} + + Consensus[protocol.ConsensusV35] = v35 + + // v33 and v34 can be upgraded to v35, with an update delay of 12h: // 10046 = (12 * 60 * 60 / 4.3) // for the sake of future manual calculations, we'll round that down a bit : - v33.ApprovedUpgrades[protocol.ConsensusV34] = 10000 + v33.ApprovedUpgrades[protocol.ConsensusV35] = 10000 + v34.ApprovedUpgrades[protocol.ConsensusV35] = 10000 // ConsensusFuture is used to test features that are implemented // but not yet released in a production protocol version. - vFuture := v34 + vFuture := v35 vFuture.ApprovedUpgrades = map[protocol.ConsensusVersion]uint64{} vFuture.LogicSigVersion = 8 // When moving this to a release, put a new higher LogicSigVersion here diff --git a/ledger/acctonline.go b/ledger/acctonline.go index 47e22c50b1..03af7908db 100644 --- a/ledger/acctonline.go +++ b/ledger/acctonline.go @@ -747,7 +747,7 @@ func (ao *onlineAccounts) lookupOnlineAccountData(rnd basics.Round, addr basics. // not participate in round == voteRnd. // See the normalization description in AccountData.NormalizedOnlineBalance(). // The return value of totalOnlineStake represents the total stake that is online for voteRnd: it is an approximation since voteRnd did not yet occur. -func (ao *onlineAccounts) TopOnlineAccounts(rnd basics.Round, voteRnd basics.Round, n uint64) (topOnlineAccounts []*ledgercore.OnlineAccount, totalOnlineStake basics.MicroAlgos, err error) { +func (ao *onlineAccounts) TopOnlineAccounts(rnd basics.Round, voteRnd basics.Round, n uint64, params *config.ConsensusParams, rewardsLevel uint64) (topOnlineAccounts []*ledgercore.OnlineAccount, totalOnlineStake basics.MicroAlgos, err error) { genesisProto := ao.ledger.GenesisProto() ao.accountsMu.RLock() for { @@ -902,6 +902,13 @@ func (ao *onlineAccounts) TopOnlineAccounts(rnd basics.Round, voteRnd basics.Rou if ot.Overflowed { return nil, basics.MicroAlgos{}, fmt.Errorf("TopOnlineAccounts: overflow in stakeOfflineInVoteRound") } + if params.StateProofExcludeTotalWeightWithRewards { + rewards := basics.PendingRewards(&ot, *params, oa.MicroAlgos, oa.RewardsBase, rewardsLevel) + totalOnlineStake = ot.SubA(totalOnlineStake, rewards) + if ot.Overflowed { + return nil, basics.MicroAlgos{}, fmt.Errorf("TopOnlineAccounts: overflow in stakeOfflineInVoteRound rewards") + } + } } return topOnlineAccounts, totalOnlineStake, nil diff --git a/ledger/acctonline_test.go b/ledger/acctonline_test.go index c1d14b43ee..8ebd160bc0 100644 --- a/ledger/acctonline_test.go +++ b/ledger/acctonline_test.go @@ -1392,7 +1392,8 @@ func TestAcctOnlineTopInBatches(t *testing.T) { _, oa := newAcctUpdates(t, ml, conf) defer oa.close() - top, _, err := oa.TopOnlineAccounts(0, 0, 2048) + proto := config.Consensus[protocol.ConsensusCurrentVersion] + top, _, err := oa.TopOnlineAccounts(0, 0, 2048, &proto, 0) a.NoError(err) compareTopAccounts(a, top, allAccts) } @@ -1437,7 +1438,8 @@ func TestAcctOnlineTopBetweenCommitAndPostCommit(t *testing.T) { defer oa.close() ml.trackers.trackers = append([]ledgerTracker{stallingTracker}, ml.trackers.trackers...) - top, _, err := oa.TopOnlineAccounts(0, 0, 5) + proto := config.Consensus[protocol.ConsensusCurrentVersion] + top, _, err := oa.TopOnlineAccounts(0, 0, 5, &proto, 0) a.NoError(err) compareTopAccounts(a, top, allAccts) @@ -1475,7 +1477,8 @@ func TestAcctOnlineTopBetweenCommitAndPostCommit(t *testing.T) { time.Sleep(2 * time.Second) stallingTracker.postCommitReleaseLock <- struct{}{} }() - top, _, err = oa.TopOnlineAccounts(2, 2, 5) + + top, _, err = oa.TopOnlineAccounts(2, 2, 5, &proto, 0) a.NoError(err) accountToBeUpdated := allAccts[numAccts-1] @@ -1528,7 +1531,8 @@ func TestAcctOnlineTopDBBehindMemRound(t *testing.T) { defer oa.close() ml.trackers.trackers = append([]ledgerTracker{stallingTracker}, ml.trackers.trackers...) - top, _, err := oa.TopOnlineAccounts(0, 0, 5) + proto := config.Consensus[protocol.ConsensusCurrentVersion] + top, _, err := oa.TopOnlineAccounts(0, 0, 5, &proto, 0) a.NoError(err) compareTopAccounts(a, top, allAccts) @@ -1571,7 +1575,8 @@ func TestAcctOnlineTopDBBehindMemRound(t *testing.T) { }) stallingTracker.postCommitReleaseLock <- struct{}{} }() - _, _, err = oa.TopOnlineAccounts(2, 2, 5) + + _, _, err = oa.TopOnlineAccounts(2, 2, 5, &proto, 0) a.Error(err) a.Contains(err.Error(), "is behind in-memory round") @@ -1680,7 +1685,8 @@ func (m *MicroAlgoOperations) Add(x, y basics.MicroAlgos) basics.MicroAlgos { } func compareOnlineTotals(a *require.Assertions, oa *onlineAccounts, rnd, voteRnd basics.Round, n uint64, expectedForRnd, expectedForVoteRnd basics.MicroAlgos) []*ledgercore.OnlineAccount { - top, onlineTotalVoteRnd, err := oa.TopOnlineAccounts(rnd, voteRnd, n) + proto := config.Consensus[protocol.ConsensusCurrentVersion] + top, onlineTotalVoteRnd, err := oa.TopOnlineAccounts(rnd, voteRnd, n, &proto, 0) a.NoError(err) a.Equal(expectedForVoteRnd, onlineTotalVoteRnd) onlineTotalsRnd, err := oa.onlineTotals(rnd) diff --git a/ledger/internal/eval_blackbox_test.go b/ledger/internal/eval_blackbox_test.go index 11c51f9aa9..8f8f7f1a35 100644 --- a/ledger/internal/eval_blackbox_test.go +++ b/ledger/internal/eval_blackbox_test.go @@ -910,6 +910,7 @@ var consensusByNumber = []protocol.ConsensusVersion{ protocol.ConsensusV32, // unlimited assets and apps protocol.ConsensusV33, // 320 rounds protocol.ConsensusV34, // AVM v7, stateproofs + protocol.ConsensusV35, // stateproofs stake fix protocol.ConsensusFuture, } diff --git a/ledger/ledgercore/votersForRound.go b/ledger/ledgercore/votersForRound.go index 4dfb39da24..1212778b31 100644 --- a/ledger/ledgercore/votersForRound.go +++ b/ledger/ledgercore/votersForRound.go @@ -18,7 +18,6 @@ package ledgercore import ( "fmt" - "github.com/algorand/go-algorand/crypto/merklesignature" "sync" "github.com/algorand/go-deadlock" @@ -26,6 +25,7 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/crypto/merklearray" + "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/crypto/stateproof" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" @@ -36,7 +36,7 @@ type OnlineAccountsFetcher interface { // TopOnlineAccounts returns the top n online accounts, sorted by their normalized // balance and address, whose voting keys are valid in voteRnd. See the // normalization description in AccountData.NormalizedOnlineBalance(). - TopOnlineAccounts(rnd basics.Round, voteRnd basics.Round, n uint64) (topOnlineAccounts []*OnlineAccount, totalOnlineStake basics.MicroAlgos, err error) + TopOnlineAccounts(rnd basics.Round, voteRnd basics.Round, n uint64, params *config.ConsensusParams, rewardsLevel uint64) (topOnlineAccounts []*OnlineAccount, totalOnlineStake basics.MicroAlgos, err error) } // VotersForRound tracks the top online voting accounts as of a particular @@ -113,7 +113,7 @@ func (tr *VotersForRound) LoadTree(onlineAccountsFetcher OnlineAccountsFetcher, // using the balances from round r. stateProofRound := r + basics.Round(tr.Proto.StateProofVotersLookback+tr.Proto.StateProofInterval) - top, totalOnlineWeight, err := onlineAccountsFetcher.TopOnlineAccounts(r, stateProofRound, tr.Proto.StateProofTopVoters) + top, totalOnlineWeight, err := onlineAccountsFetcher.TopOnlineAccounts(r, stateProofRound, tr.Proto.StateProofTopVoters, &tr.Proto, hdr.RewardsLevel) if err != nil { return err } diff --git a/protocol/consensus.go b/protocol/consensus.go index 11a5c5f07c..a996525ad5 100644 --- a/protocol/consensus.go +++ b/protocol/consensus.go @@ -187,6 +187,11 @@ const ConsensusV34 = ConsensusVersion( "https://github.com/algorandfoundation/specs/tree/2dd5435993f6f6d65691140f592ebca5ef19ffbd", ) +// ConsensusV35 updates the calculation of total stake in state proofs. +const ConsensusV35 = ConsensusVersion( + "https://github.com/algorandfoundation/specs/tree/433d8e9a7274b6fca703d91213e05c7e6a589e69", +) + // ConsensusFuture is a protocol that should not appear in any production // network, but is used to test features before they are released. const ConsensusFuture = ConsensusVersion( @@ -199,7 +204,7 @@ const ConsensusFuture = ConsensusVersion( // ConsensusCurrentVersion is the latest version and should be used // when a specific version is not provided. -const ConsensusCurrentVersion = ConsensusV34 +const ConsensusCurrentVersion = ConsensusV35 // Error is used to indicate that an unsupported protocol has been detected. type Error ConsensusVersion diff --git a/test/e2e-go/features/stateproofs/stateproofs_test.go b/test/e2e-go/features/stateproofs/stateproofs_test.go index b2500d0a56..c82ac8b9a1 100644 --- a/test/e2e-go/features/stateproofs/stateproofs_test.go +++ b/test/e2e-go/features/stateproofs/stateproofs_test.go @@ -17,10 +17,12 @@ package stateproofs import ( + "bytes" "fmt" "os" "path/filepath" "runtime" + "strings" "sync" "sync/atomic" "testing" @@ -34,6 +36,8 @@ import ( "github.com/algorand/go-algorand/crypto/merklearray" sp "github.com/algorand/go-algorand/crypto/stateproof" "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated" + generatedV2 "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated" + v1 "github.com/algorand/go-algorand/daemon/algod/api/spec/v1" "github.com/algorand/go-algorand/data/account" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" @@ -122,8 +126,12 @@ func TestStateProofs(t *testing.T) { } func TestStateProofsMultiWallets(t *testing.T) { - t.Skip("this test is heavy and should be run manually") partitiontest.PartitionTest(t) + + if strings.ToUpper(os.Getenv("CIRCLECI")) == "TRUE" { + t.Skip() + } + defer fixtures.ShutdownSynchronizedTest(t) configurableConsensus := make(config.ConsensusProtocols) @@ -1155,3 +1163,140 @@ func getWellformedSPTransaction(round uint64, genesisHash crypto.Digest, consens return stxn } + +func TestStateProofCheckTotalStake(t *testing.T) { + partitiontest.PartitionTest(t) + + if strings.ToUpper(os.Getenv("CIRCLECI")) == "TRUE" { + t.Skip() + } + + defer fixtures.ShutdownSynchronizedTest(t) + + r := require.New(fixtures.SynchronizedTest(t)) + + configurableConsensus := make(config.ConsensusProtocols) + consensusVersion := protocol.ConsensusVersion("test-fast-stateproofs") + consensusParams := getDefaultStateProofConsensusParams() + consensusParams.StateProofWeightThreshold = (1 << 32) * 90 / 100 + consensusParams.StateProofStrengthTarget = 3 + consensusParams.AgreementFilterTimeout = 1000 * time.Millisecond + consensusParams.AgreementFilterTimeoutPeriod0 = 1000 * time.Millisecond + consensusParams.SeedLookback = 2 + consensusParams.SeedRefreshInterval = 8 + consensusParams.MaxBalLookback = 2 * consensusParams.SeedLookback * consensusParams.SeedRefreshInterval // 32 + configurableConsensus[consensusVersion] = consensusParams + + var fixture fixtures.RestClientFixture + pNodes := 5 + expectedNumberOfStateProofs := uint64(4) + fixture.SetConsensus(configurableConsensus) + fixture.Setup(t, filepath.Join("nettemplates", "StateProof.json")) + + defer fixture.Shutdown() + + // Get node libgoal clients in order to update their participation keys + libgoalNodeClients := make([]libgoal.Client, pNodes, pNodes) + accountsAddresses := make([]string, pNodes, pNodes) + for i := 0; i < pNodes; i++ { + nodeName := fmt.Sprintf("Node%d", i) + libgoalNodeClients[i] = fixture.GetLibGoalClientForNamedNode(nodeName) + parts, err := libgoalNodeClients[i].GetParticipationKeys() + r.NoError(err) + accountsAddresses[i] = parts[0].Address + } + + participations := make([]account.Participation, pNodes, pNodes) + var lastStateProofBlock bookkeeping.Block + libgoalClient := fixture.LibGoalClient + + var totalSupplyAtRound [100]v1.Supply + var accountSnapshotAtRound [100][]generatedV2.Account + + for rnd := uint64(1); rnd <= consensusParams.StateProofInterval*(expectedNumberOfStateProofs+1); rnd++ { + if rnd == consensusParams.StateProofInterval+consensusParams.StateProofVotersLookback { // here we register the keys of address 0 so it won't be able the sign a state proof (its stake would be removed for the total) + _, part, err := installParticipationKey(t, libgoalNodeClients[0], accountsAddresses[0], 0, consensusParams.StateProofInterval*2-1) + r.NoError(err) + participations[0] = part + registerParticipationAndWait(t, libgoalNodeClients[0], participations[0]) + } + + //send a dummy payment transaction. + paymentSender{ + from: accountFetcher{nodeName: "Node3", accountNumber: 0}, + to: accountFetcher{nodeName: "Node4", accountNumber: 0}, + amount: 1, + }.sendPayment(r, &fixture, rnd) + + err := fixture.WaitForRound(rnd, timeoutUntilNextRound) + r.NoError(err) + + // this is the round in we take a snapshot of the account balances. + // We would use this snapshot later on to compare the weights on the state proof, and to make sure that + // the totalWeight commitment is correct + if ((rnd + 2) % consensusParams.StateProofInterval) == 0 { + totalSupply, err := libgoalClient.LedgerSupply() + r.NoError(err) + + r.Equal(rnd, totalSupply.Round, "could not capture total stake at the target round. The machine might be too slow for this test") + totalSupplyAtRound[rnd] = totalSupply + + accountSnapshotAtRound[rnd] = make([]generatedV2.Account, pNodes, pNodes) + for i := 0; i < pNodes; i++ { + accountSnapshotAtRound[rnd][i], err = libgoalClient.AccountInformationV2(accountsAddresses[i], false) + r.NoError(err) + r.NotEqual(accountSnapshotAtRound[rnd][i].Amount, uint64(0)) + r.Equal(rnd, accountSnapshotAtRound[rnd][i].Round, "could not capture the account at the target round. The machine might be too slow for this test") + } + } + + blk, err := libgoalClient.BookkeepingBlock(rnd) + r.NoErrorf(err, "failed to retrieve block from algod on round %d", rnd) + + if (rnd % consensusParams.StateProofInterval) == 0 { + if rnd >= consensusParams.StateProofInterval*2 { + // since account 0 would no longer be able to sign the state proof, its stake should + // be removed from the total stake in the commitment + total := totalSupplyAtRound[rnd-consensusParams.StateProofVotersLookback].OnlineMoney + total = total - accountSnapshotAtRound[rnd-consensusParams.StateProofVotersLookback][0].Amount + r.Equal(total, blk.StateProofTracking[protocol.StateProofBasic].StateProofOnlineTotalWeight.Raw) + } else { + r.Equal(totalSupplyAtRound[rnd-consensusParams.StateProofVotersLookback].OnlineMoney, blk.StateProofTracking[protocol.StateProofBasic].StateProofOnlineTotalWeight.Raw) + } + + // Special case: bootstrap validation with the first block + // that has a merkle root. + if lastStateProofBlock.Round() == 0 { + lastStateProofBlock = blk + } + } + + for lastStateProofBlock.Round() != 0 && lastStateProofBlock.Round()+basics.Round(consensusParams.StateProofInterval) < blk.StateProofTracking[protocol.StateProofBasic].StateProofNextRound { + nextStateProofRound := uint64(lastStateProofBlock.Round()) + consensusParams.StateProofInterval + + t.Logf("found a state proof for round %d at round %d", nextStateProofRound, blk.Round()) + + stateProof, stateProofMsg := getStateProofByLastRound(r, &fixture, nextStateProofRound, expectedNumberOfStateProofs) + + accountSnapshot := accountSnapshotAtRound[stateProofMsg.LastAttestedRound-consensusParams.StateProofInterval-consensusParams.StateProofVotersLookback] + + // once the state proof is accepted we want to make sure that the weight + for _, v := range stateProof.Reveals { + found := false + for i := 0; i < len(accountSnapshot); i++ { + if bytes.Compare(v.Part.PK.Commitment[:], *accountSnapshot[i].Participation.StateProofKey) == 0 { + r.Equal(v.Part.Weight, accountSnapshot[i].Amount) + found = true + break + } + } + r.True(found) + } + nextStateProofBlock, err := fixture.LibGoalClient.BookkeepingBlock(nextStateProofRound) + r.NoError(err) + lastStateProofBlock = nextStateProofBlock + } + } + + r.Equalf(int(consensusParams.StateProofInterval*expectedNumberOfStateProofs), int(lastStateProofBlock.Round()), "the expected last state proof block wasn't the one that was observed") +} diff --git a/test/e2e-go/upgrades/send_receive_upgrade_test.go b/test/e2e-go/upgrades/send_receive_upgrade_test.go index c69f4f3ca7..a3992872a9 100644 --- a/test/e2e-go/upgrades/send_receive_upgrade_test.go +++ b/test/e2e-go/upgrades/send_receive_upgrade_test.go @@ -47,35 +47,43 @@ func TestAccountsCanSendMoneyAcrossUpgradeV15toV16(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) - testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV15Upgrade.json")) + testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV15Upgrade.json"), "") } func TestAccountsCanSendMoneyAcrossUpgradeV21toV22(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) - testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV21Upgrade.json")) + testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV21Upgrade.json"), "") } func TestAccountsCanSendMoneyAcrossUpgradeV22toV23(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) - testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV22Upgrade.json")) + testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV22Upgrade.json"), "") } func TestAccountsCanSendMoneyAcrossUpgradeV23toV24(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) - testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV23Upgrade.json")) + testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV23Upgrade.json"), "") } func TestAccountsCanSendMoneyAcrossUpgradeV24toV25(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) - testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV24Upgrade.json")) + testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV24Upgrade.json"), "") +} + +func TestAccountsCanSendMoneyAcrossUpgradeV32toV35(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + targetVersion := consensusTestFastUpgrade(protocol.ConsensusV35) + testAccountsCanSendMoneyAcrossUpgrade(t, filepath.Join("nettemplates", "TwoNodes50EachV32Upgrade.json"), targetVersion) } // ConsensusTestFastUpgrade is meant for testing of protocol upgrades: @@ -112,7 +120,7 @@ func generateFastUpgradeConsensus() (fastUpgradeProtocols config.ConsensusProtoc return } -func testAccountsCanSendMoneyAcrossUpgrade(t *testing.T, templatePath string) { +func testAccountsCanSendMoneyAcrossUpgrade(t *testing.T, templatePath string, targetVersion protocol.ConsensusVersion) { //t.Parallel() a := require.New(fixtures.SynchronizedTest(t)) @@ -123,17 +131,17 @@ func testAccountsCanSendMoneyAcrossUpgrade(t *testing.T, templatePath string) { fixture.Setup(t, templatePath) defer fixture.Shutdown() - verifyAccountsCanSendMoneyAcrossUpgrade(a, &fixture) + verifyAccountsCanSendMoneyAcrossUpgrade(a, &fixture, targetVersion) } -func verifyAccountsCanSendMoneyAcrossUpgrade(a *require.Assertions, fixture *fixtures.RestClientFixture) { - pingBalance, pongBalance, expectedPingBalance, expectedPongBalance := runUntilProtocolUpgrades(a, fixture) +func verifyAccountsCanSendMoneyAcrossUpgrade(a *require.Assertions, fixture *fixtures.RestClientFixture, targetVersion protocol.ConsensusVersion) { + pingBalance, pongBalance, expectedPingBalance, expectedPongBalance := runUntilProtocolUpgrades(a, fixture, targetVersion) a.True(expectedPingBalance <= pingBalance, "ping balance is different than expected") a.True(expectedPongBalance <= pongBalance, "pong balance is different than expected") } -func runUntilProtocolUpgrades(a *require.Assertions, fixture *fixtures.RestClientFixture) (uint64, uint64, uint64, uint64) { +func runUntilProtocolUpgrades(a *require.Assertions, fixture *fixtures.RestClientFixture, targetVersion protocol.ConsensusVersion) (uint64, uint64, uint64, uint64) { c := fixture.LibGoalClient initialStatus, err := c.Status() a.NoError(err, "getting status") @@ -152,7 +160,9 @@ func runUntilProtocolUpgrades(a *require.Assertions, fixture *fixtures.RestClien pongAccount := pongAccountList[0] pingBalance, err := c.GetBalance(pingAccount) + a.NoError(err) pongBalance, err := c.GetBalance(pongAccount) + a.NoError(err) a.Equal(pingBalance, pongBalance, "both accounts should start with same balance") a.NotEqual(pingAccount, pongAccount, "accounts under study should be different") @@ -200,7 +210,7 @@ func runUntilProtocolUpgrades(a *require.Assertions, fixture *fixtures.RestClien pingWalletHandle, err = pingClient.GetUnencryptedWalletHandle() a.NoError(err) - iterationDuration := time.Now().Sub(iterationStartTime) + iterationDuration := time.Since(iterationStartTime) if iterationDuration < 500*time.Millisecond { time.Sleep(500*time.Millisecond - iterationDuration) } @@ -210,6 +220,20 @@ func runUntilProtocolUpgrades(a *require.Assertions, fixture *fixtures.RestClien } } + // optionally wait until the target version if set + startTime = time.Now() + if targetVersion != protocol.ConsensusVersion("") { + for curStatus.LastVersion != string(targetVersion) { + time.Sleep(500 * time.Millisecond) + + if time.Now().After(startTime.Add(5 * time.Minute)) { + a.Fail("upgrade to target taking too long") + } + curStatus, err = pongClient.Status() + a.NoError(err) + } + } + initialStatus, err = c.Status() a.NoError(err, "getting status") @@ -243,7 +267,7 @@ func runUntilProtocolUpgrades(a *require.Assertions, fixture *fixtures.RestClien pingWalletHandle, err = pingClient.GetUnencryptedWalletHandle() a.NoError(err) - iterationDuration := time.Now().Sub(iterationStartTime) + iterationDuration := time.Since(iterationStartTime) if iterationDuration < 500*time.Millisecond { time.Sleep(500*time.Millisecond - iterationDuration) } diff --git a/test/testdata/nettemplates/TwoNodes50EachV32Upgrade.json b/test/testdata/nettemplates/TwoNodes50EachV32Upgrade.json new file mode 100644 index 0000000000..68cc158788 --- /dev/null +++ b/test/testdata/nettemplates/TwoNodes50EachV32Upgrade.json @@ -0,0 +1,36 @@ +{ + "Genesis": { + "NetworkName": "tbd", + "ConsensusProtocol": "test-fast-upgrade-https://github.com/algorandfoundation/specs/tree/d5ac876d7ede07367dbaa26e149aa42589aac1f7", + "LastPartKeyRound": 3000, + "Wallets": [ + { + "Name": "Wallet1", + "Stake": 50, + "Online": true + }, + { + "Name": "Wallet2", + "Stake": 50, + "Online": true + } + ] + }, + "Nodes": [ + { + "Name": "Primary", + "IsRelay": true, + "Wallets": [ + { "Name": "Wallet1", + "ParticipationOnly": false } + ] + }, + { + "Name": "Node", + "Wallets": [ + { "Name": "Wallet2", + "ParticipationOnly": false } + ] + } + ] +}