summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Lee <64482439+algojohnlee@users.noreply.github.com>2023-02-03 13:00:03 -0500
committerGitHub <noreply@github.com>2023-02-03 13:00:03 -0500
commit8fd6e702f2b319da5e404815f8af7a402e2a67f1 (patch)
tree03f724c768201a61b984165117a622d1aa8f12b8
parent9ddd136dd926db354ce28344cb7596f3bcecdb42 (diff)
parent90e744c3db8427d91f03d1275bbfa9b6dde10fc8 (diff)
Merge pull request #5110 from Algo-devops-service/relstable3.14.2v3.14.2-stable
-rw-r--r--.circleci/config.yml1
-rw-r--r--.github/workflows/container.yml57
-rw-r--r--.github/workflows/reviewdog.yml38
-rw-r--r--.golangci-warnings.yml4
-rw-r--r--.golangci.yml56
-rw-r--r--Dockerfile87
-rw-r--r--Makefile2
-rw-r--r--README.md2
-rw-r--r--agreement/abstractions.go2
-rw-r--r--agreement/actions.go2
-rw-r--r--agreement/actor.go2
-rw-r--r--agreement/agreeInstall.go2
-rw-r--r--agreement/agreementtest/keyManager.go2
-rw-r--r--agreement/agreementtest/simulate.go7
-rw-r--r--agreement/agreementtest/simulate_test.go5
-rw-r--r--agreement/asyncVoteVerifier.go2
-rw-r--r--agreement/asyncVoteVerifier_test.go5
-rw-r--r--agreement/autopsy.go2
-rw-r--r--agreement/bundle.go2
-rw-r--r--agreement/bundle_test.go2
-rw-r--r--agreement/cadaver.go13
-rw-r--r--agreement/certificate.go2
-rw-r--r--agreement/certificate_test.go2
-rw-r--r--agreement/common_test.go2
-rw-r--r--agreement/coservice.go2
-rw-r--r--agreement/cryptoRequestContext.go2
-rw-r--r--agreement/cryptoRequestContext_test.go2
-rw-r--r--agreement/cryptoVerifier.go2
-rw-r--r--agreement/cryptoVerifier_test.go2
-rw-r--r--agreement/demux.go2
-rw-r--r--agreement/demux_test.go2
-rw-r--r--agreement/doc.go2
-rw-r--r--agreement/encoding_test.go2
-rw-r--r--agreement/errors.go2
-rw-r--r--agreement/events.go4
-rw-r--r--agreement/events_test.go4
-rw-r--r--agreement/fuzzer/bandwidthFilter_test.go2
-rw-r--r--agreement/fuzzer/catchupFilter_test.go2
-rw-r--r--agreement/fuzzer/clockedFilter_test.go2
-rw-r--r--agreement/fuzzer/dropMessageFilter_test.go2
-rw-r--r--agreement/fuzzer/duplicateMessageFilter_test.go2
-rw-r--r--agreement/fuzzer/filter_test.go2
-rw-r--r--agreement/fuzzer/fuzzer.go2
-rw-r--r--agreement/fuzzer/fuzzer_test.go13
-rw-r--r--agreement/fuzzer/ledger_test.go2
-rw-r--r--agreement/fuzzer/messageDecoderFilter_test.go2
-rw-r--r--agreement/fuzzer/messageDelayFilter_test.go2
-rw-r--r--agreement/fuzzer/messageDuplicationFilter_test.go2
-rw-r--r--agreement/fuzzer/messagePriorityQueue_test.go2
-rw-r--r--agreement/fuzzer/messageReflectionFilter_test.go2
-rw-r--r--agreement/fuzzer/messageRegossipFilter_test.go2
-rw-r--r--agreement/fuzzer/messageReorderingFilter_test.go2
-rw-r--r--agreement/fuzzer/networkFacade_test.go2
-rw-r--r--agreement/fuzzer/nodeCrashFilter_test.go2
-rw-r--r--agreement/fuzzer/nullFilter_test.go2
-rw-r--r--agreement/fuzzer/router_test.go2
-rw-r--r--agreement/fuzzer/schedulerFilter_test.go2
-rw-r--r--agreement/fuzzer/tests_test.go2
-rw-r--r--agreement/fuzzer/topologyFilter_test.go2
-rw-r--r--agreement/fuzzer/trafficStatisticsFilter_test.go2
-rw-r--r--agreement/fuzzer/validator_test.go2
-rw-r--r--agreement/fuzzer/voteFilter_test.go2
-rw-r--r--agreement/gossip/network.go2
-rw-r--r--agreement/gossip/networkFull_test.go2
-rw-r--r--agreement/gossip/network_test.go2
-rw-r--r--agreement/keyManager_test.go2
-rw-r--r--agreement/listener.go2
-rw-r--r--agreement/message.go2
-rw-r--r--agreement/message_test.go2
-rw-r--r--agreement/msgp_gen.go174
-rw-r--r--agreement/params.go2
-rw-r--r--agreement/persistence.go2
-rw-r--r--agreement/persistence_test.go3
-rw-r--r--agreement/player.go2
-rw-r--r--agreement/playerContract.go2
-rw-r--r--agreement/player_permutation_test.go2
-rw-r--r--agreement/player_test.go88
-rw-r--r--agreement/proposal.go7
-rw-r--r--agreement/proposalManager.go2
-rw-r--r--agreement/proposalManagerContract.go2
-rw-r--r--agreement/proposalManager_test.go2
-rw-r--r--agreement/proposalStore.go5
-rw-r--r--agreement/proposalStoreContract.go2
-rw-r--r--agreement/proposalStore_test.go2
-rw-r--r--agreement/proposalTable.go2
-rw-r--r--agreement/proposalTable_test.go2
-rw-r--r--agreement/proposalTracker.go2
-rw-r--r--agreement/proposalTrackerContract.go2
-rw-r--r--agreement/proposalTracker_test.go2
-rw-r--r--agreement/proposal_test.go2
-rw-r--r--agreement/pseudonode.go2
-rw-r--r--agreement/pseudonode_test.go2
-rw-r--r--agreement/router.go2
-rw-r--r--agreement/selector.go2
-rw-r--r--agreement/service.go12
-rw-r--r--agreement/service_test.go5
-rw-r--r--agreement/sort.go2
-rw-r--r--agreement/sort_test.go2
-rw-r--r--agreement/state_machine_test.go2
-rw-r--r--agreement/trace.go17
-rw-r--r--agreement/traceTime.go2
-rw-r--r--agreement/types.go2
-rw-r--r--agreement/vote.go2
-rw-r--r--agreement/voteAggregator.go2
-rw-r--r--agreement/voteAggregatorContract.go2
-rw-r--r--agreement/voteAggregator_test.go2
-rw-r--r--agreement/voteAuxiliary.go2
-rw-r--r--agreement/voteAuxiliaryContract.go2
-rw-r--r--agreement/voteAuxiliary_test.go2
-rw-r--r--agreement/voteTracker.go2
-rw-r--r--agreement/voteTrackerContract.go2
-rw-r--r--agreement/voteTracker_test.go2
-rw-r--r--agreement/vote_test.go2
-rw-r--r--buildnumber.dat2
-rw-r--r--catchup/catchpointService.go2
-rw-r--r--catchup/catchpointService_test.go2
-rw-r--r--catchup/fetcher_test.go2
-rw-r--r--catchup/ledgerFetcher.go5
-rw-r--r--catchup/ledgerFetcher_test.go2
-rw-r--r--catchup/networkFetcher.go134
-rw-r--r--catchup/networkFetcher_test.go190
-rw-r--r--catchup/peerSelector.go2
-rw-r--r--catchup/peerSelector_test.go2
-rw-r--r--catchup/pref_test.go2
-rw-r--r--catchup/service.go2
-rw-r--r--catchup/service_test.go2
-rw-r--r--catchup/universalFetcher.go2
-rw-r--r--catchup/universalFetcher_test.go2
-rw-r--r--cmd/algocfg/datadir.go2
-rw-r--r--cmd/algocfg/getCommand.go4
-rw-r--r--cmd/algocfg/getCommand_test.go2
-rw-r--r--cmd/algocfg/main.go2
-rw-r--r--cmd/algocfg/messages.go2
-rw-r--r--cmd/algocfg/report.go2
-rw-r--r--cmd/algocfg/resetCommand.go4
-rw-r--r--cmd/algocfg/setCommand.go4
-rw-r--r--cmd/algod/main.go14
-rw-r--r--cmd/algod/main_test.go2
-rw-r--r--cmd/algofix/deadlock.go2
-rw-r--r--cmd/algofix/deadlock_test.go2
-rw-r--r--cmd/algoh/blockWatcher.go2
-rw-r--r--cmd/algoh/blockWatcher_test.go2
-rw-r--r--cmd/algoh/blockstats.go2
-rw-r--r--cmd/algoh/blockstats_test.go2
-rw-r--r--cmd/algoh/client.go2
-rw-r--r--cmd/algoh/deadman.go2
-rw-r--r--cmd/algoh/eventsender.go2
-rw-r--r--cmd/algoh/main.go2
-rw-r--r--cmd/algoh/mockClient.go2
-rw-r--r--cmd/algokey/common.go2
-rw-r--r--cmd/algokey/export.go2
-rw-r--r--cmd/algokey/generate.go2
-rw-r--r--cmd/algokey/import.go2
-rw-r--r--cmd/algokey/keyreg.go2
-rw-r--r--cmd/algokey/main.go2
-rw-r--r--cmd/algokey/multisig.go2
-rw-r--r--cmd/algokey/part.go2
-rw-r--r--cmd/algokey/sign.go2
-rw-r--r--cmd/algons/commands.go2
-rw-r--r--cmd/algons/dnsCmd.go2
-rw-r--r--cmd/algorelay/commands.go2
-rw-r--r--cmd/algorelay/eb/eb.go2
-rw-r--r--cmd/algorelay/relayCmd.go2
-rw-r--r--cmd/buildtools/commands.go2
-rw-r--r--cmd/buildtools/genesis.go2
-rw-r--r--cmd/catchpointdump/commands.go2
-rw-r--r--cmd/catchpointdump/database.go2
-rw-r--r--cmd/catchpointdump/file.go6
-rw-r--r--cmd/catchpointdump/net.go2
-rw-r--r--cmd/catchupsrv/download.go2
-rw-r--r--cmd/catchupsrv/download_test.go2
-rw-r--r--cmd/catchupsrv/main.go2
-rw-r--r--cmd/catchupsrv/tarblocks.go2
-rw-r--r--cmd/dbgen/main.go2
-rw-r--r--cmd/diagcfg/main.go2
-rw-r--r--cmd/diagcfg/messages.go2
-rw-r--r--cmd/diagcfg/metric.go2
-rw-r--r--cmd/diagcfg/telemetry.go2
-rw-r--r--cmd/dispenser/server.go2
-rw-r--r--cmd/genesis/newgenesis.go2
-rw-r--r--cmd/goal/account.go2
-rw-r--r--cmd/goal/accountsList.go2
-rw-r--r--cmd/goal/application.go25
-rw-r--r--cmd/goal/application_test.go2
-rw-r--r--cmd/goal/asset.go2
-rw-r--r--cmd/goal/box.go2
-rw-r--r--cmd/goal/clerk.go8
-rw-r--r--cmd/goal/commands.go2
-rw-r--r--cmd/goal/commands_test.go2
-rw-r--r--cmd/goal/common.go2
-rw-r--r--cmd/goal/completion.go2
-rw-r--r--cmd/goal/formatting.go2
-rw-r--r--cmd/goal/formatting_test.go3
-rw-r--r--cmd/goal/inspect.go2
-rw-r--r--cmd/goal/inspect_test.go2
-rw-r--r--cmd/goal/interact.go5
-rw-r--r--cmd/goal/kmd.go2
-rw-r--r--cmd/goal/ledger.go2
-rw-r--r--cmd/goal/logging.go2
-rw-r--r--cmd/goal/messages.go70
-rw-r--r--cmd/goal/multisig.go4
-rw-r--r--cmd/goal/network.go2
-rw-r--r--cmd/goal/node.go34
-rw-r--r--cmd/goal/node_test.go2
-rw-r--r--cmd/goal/tealsign.go2
-rw-r--r--cmd/goal/wallet.go2
-rw-r--r--cmd/incorporate/incorporate.go2
-rw-r--r--cmd/kmd/codes/codes.go2
-rw-r--r--cmd/kmd/main.go2
-rw-r--r--cmd/kmd/mlock_darwin.go2
-rw-r--r--cmd/kmd/mlock_linux.go2
-rw-r--r--cmd/kmd/mlock_windows.go2
-rw-r--r--cmd/loadgenerator/config.go2
-rw-r--r--cmd/loadgenerator/main.go2
-rw-r--r--cmd/msgpacktool/main.go2
-rw-r--r--cmd/netdummy/main.go2
-rw-r--r--cmd/netgoal/commands.go2
-rw-r--r--cmd/netgoal/generate.go2
-rw-r--r--cmd/netgoal/messages.go2
-rw-r--r--cmd/netgoal/network.go2
-rw-r--r--cmd/netgoal/recipe.go2
-rw-r--r--cmd/nodecfg/apply.go7
-rw-r--r--cmd/nodecfg/commands.go2
-rw-r--r--cmd/nodecfg/download.go2
-rw-r--r--cmd/nodecfg/get.go2
-rw-r--r--cmd/opdoc/opdoc.go29
-rw-r--r--cmd/opdoc/tmLanguage.go2
-rw-r--r--cmd/partitiontest_linter/go.mod9
-rw-r--r--cmd/partitiontest_linter/go.sum33
-rw-r--r--cmd/partitiontest_linter/linter.go4
-rw-r--r--cmd/partitiontest_linter/linter_test.go2
-rw-r--r--cmd/partitiontest_linter/plugin/plugin.go2
-rw-r--r--cmd/partitiontest_linter/testdata/linter_testdata_test.go2
-rw-r--r--cmd/pingpong/commands.go2
-rw-r--r--cmd/pingpong/runCmd.go10
-rw-r--r--cmd/pingpong/teal_programs.go2
-rw-r--r--cmd/tealdbg/README.md20
-rwxr-xr-xcmd/tealdbg/bundle_home_html.sh2
-rw-r--r--cmd/tealdbg/cdt/proto.go2
-rw-r--r--cmd/tealdbg/cdtSession.go2
-rw-r--r--cmd/tealdbg/cdtSession_test.go2
-rw-r--r--cmd/tealdbg/cdtState.go2
-rw-r--r--cmd/tealdbg/cdtStateObjects.go2
-rw-r--r--cmd/tealdbg/cdtdbg.go2
-rw-r--r--cmd/tealdbg/cdtdbg_test.go2
-rw-r--r--cmd/tealdbg/debugger.go25
-rw-r--r--cmd/tealdbg/debugger_test.go4
-rw-r--r--cmd/tealdbg/dryrunRequest.go19
-rw-r--r--cmd/tealdbg/homepage.go2
-rw-r--r--cmd/tealdbg/local.go4
-rw-r--r--cmd/tealdbg/localLedger.go2
-rw-r--r--cmd/tealdbg/localLedger_test.go2
-rw-r--r--cmd/tealdbg/local_test.go2
-rw-r--r--cmd/tealdbg/main.go2
-rw-r--r--cmd/tealdbg/remote.go16
-rw-r--r--cmd/tealdbg/remote_test.go2
-rw-r--r--cmd/tealdbg/server.go2
-rw-r--r--cmd/tealdbg/server_test.go2
-rw-r--r--cmd/tealdbg/util.go2
-rw-r--r--cmd/tealdbg/webdbg.go2
-rw-r--r--cmd/tealdbg/webdbg_test.go2
-rw-r--r--cmd/updater/commands.go2
-rw-r--r--cmd/updater/sendCmd.go2
-rw-r--r--cmd/updater/toolsCmd.go2
-rwxr-xr-xcmd/updater/update.sh2
-rw-r--r--cmd/updater/util.go2
-rw-r--r--cmd/updater/versionCmd.go2
-rw-r--r--cmd/updater/version_test.go2
-rw-r--r--cmd/util/cmd.go2
-rw-r--r--components/mocks/mockCatchpointCatchupAccessor.go2
-rw-r--r--components/mocks/mockNetwork.go2
-rw-r--r--components/mocks/mockParticipationRegistry.go2
-rw-r--r--config/buildvars.go2
-rw-r--r--config/config.go12
-rw-r--r--config/config_test.go157
-rw-r--r--config/consensus.go2
-rw-r--r--config/consensus_test.go2
-rw-r--r--config/defaultsGenerator/defaultsGenerator.go2
-rw-r--r--config/keyfile.go2
-rw-r--r--config/localTemplate.go75
-rw-r--r--config/local_defaults.go21
-rw-r--r--config/migrate.go2
-rw-r--r--config/version.go4
-rw-r--r--crypto/batchverifier.go18
-rw-r--r--crypto/batchverifier_test.go74
-rw-r--r--crypto/crypto_test.go2
-rw-r--r--crypto/cryptoerror.go2
-rw-r--r--crypto/curve25519.go2
-rw-r--r--crypto/curve25519_test.go2
-rw-r--r--crypto/digest.go2
-rw-r--r--crypto/encoding_test.go2
-rw-r--r--crypto/falconWrapper.go2
-rw-r--r--crypto/falconWrapper_test.go5
-rw-r--r--crypto/hashes.go2
-rw-r--r--crypto/hashes_test.go2
-rw-r--r--crypto/memcpy_chk_windows.c2
-rw-r--r--crypto/merklearray/array.go2
-rw-r--r--crypto/merklearray/layer.go2
-rw-r--r--crypto/merklearray/merkle.go2
-rw-r--r--crypto/merklearray/merkle_test.go2
-rw-r--r--crypto/merklearray/partial.go2
-rw-r--r--crypto/merklearray/proof.go2
-rw-r--r--crypto/merklearray/proof_test.go2
-rw-r--r--crypto/merklearray/vectorCommitmentArray.go2
-rw-r--r--crypto/merklearray/vectorCommitmentArray_test.go2
-rw-r--r--crypto/merklearray/worker.go2
-rw-r--r--crypto/merklesignature/committablePublicKeys.go4
-rw-r--r--crypto/merklesignature/committablePublicKeys_test.go2
-rw-r--r--crypto/merklesignature/const.go2
-rw-r--r--crypto/merklesignature/kats_test.go2
-rw-r--r--crypto/merklesignature/keysBuilder.go2
-rw-r--r--crypto/merklesignature/keysBuilder_test.go2
-rw-r--r--crypto/merklesignature/merkleSignatureScheme.go2
-rw-r--r--crypto/merklesignature/merkleSignatureScheme_test.go2
-rw-r--r--crypto/merklesignature/persistentMerkleSignatureScheme.go2
-rw-r--r--crypto/merklesignature/persistentMerkleSignatureScheme_test.go2
-rw-r--r--crypto/merklesignature/posdivs.go2
-rw-r--r--crypto/merklesignature/posdivs_test.go2
-rw-r--r--crypto/merkletrie/bitset.go2
-rw-r--r--crypto/merkletrie/bitset_test.go2
-rw-r--r--crypto/merkletrie/cache.go2
-rw-r--r--crypto/merkletrie/cache_test.go2
-rw-r--r--crypto/merkletrie/committer.go2
-rw-r--r--crypto/merkletrie/committer_test.go2
-rw-r--r--crypto/merkletrie/node.go2
-rw-r--r--crypto/merkletrie/node_test.go2
-rw-r--r--crypto/merkletrie/trie.go2
-rw-r--r--crypto/merkletrie/trie_test.go2
-rw-r--r--crypto/multisig.go2
-rw-r--r--crypto/multisig_test.go2
-rw-r--r--crypto/onetimesig.go2
-rw-r--r--crypto/onetimesig_test.go2
-rw-r--r--crypto/passphrase/errors.go2
-rw-r--r--crypto/passphrase/passphrase.go2
-rw-r--r--crypto/passphrase/passphrase_test.go2
-rw-r--r--crypto/passphrase/wordlist.go2
-rw-r--r--crypto/rand.go2
-rw-r--r--crypto/rand_test.go2
-rw-r--r--crypto/stateproof/builder.go2
-rw-r--r--crypto/stateproof/builder_test.go2
-rw-r--r--crypto/stateproof/coinGenerator.go2
-rw-r--r--crypto/stateproof/coinGenerator_test.go2
-rw-r--r--crypto/stateproof/committableSignatureSlot.go2
-rw-r--r--crypto/stateproof/committableSignatureSlot_test.go2
-rw-r--r--crypto/stateproof/const.go2
-rw-r--r--crypto/stateproof/structs.go2
-rw-r--r--crypto/stateproof/verifier.go2
-rw-r--r--crypto/stateproof/verifier_test.go2
-rw-r--r--crypto/stateproof/weights.go2
-rw-r--r--crypto/stateproof/weights_test.go2
-rw-r--r--crypto/util.go2
-rw-r--r--crypto/util_test.go2
-rw-r--r--crypto/vrf.go2
-rw-r--r--crypto/vrf_test.go2
-rw-r--r--daemon/algod/api/Makefile5
-rw-r--r--daemon/algod/api/README.md10
-rw-r--r--daemon/algod/api/algod.oas2.json122
-rw-r--r--daemon/algod/api/algod.oas3.yml217
-rw-r--r--daemon/algod/api/client/encoding.go2
-rw-r--r--daemon/algod/api/client/restClient.go2
-rw-r--r--daemon/algod/api/server/common/handlers.go2
-rw-r--r--daemon/algod/api/server/common/metrics.go2
-rw-r--r--daemon/algod/api/server/common/responses.go2
-rw-r--r--daemon/algod/api/server/common/routes.go2
-rw-r--r--daemon/algod/api/server/lib/common.go2
-rw-r--r--daemon/algod/api/server/lib/middlewares/auth.go2
-rw-r--r--daemon/algod/api/server/lib/middlewares/auth_test.go2
-rw-r--r--daemon/algod/api/server/lib/middlewares/connectionLimiter.go2
-rw-r--r--daemon/algod/api/server/lib/middlewares/connectionLimiter_test.go2
-rw-r--r--daemon/algod/api/server/lib/middlewares/cors.go2
-rw-r--r--daemon/algod/api/server/lib/middlewares/logger.go2
-rw-r--r--daemon/algod/api/server/router.go7
-rw-r--r--daemon/algod/api/server/router_test.go2
-rw-r--r--daemon/algod/api/server/v1/handlers/errors.go2
-rw-r--r--daemon/algod/api/server/v1/handlers/handlers.go2
-rw-r--r--daemon/algod/api/server/v1/routes/routes.go2
-rw-r--r--daemon/algod/api/server/v2/account.go2
-rw-r--r--daemon/algod/api/server/v2/account_test.go2
-rw-r--r--daemon/algod/api/server/v2/delta.go2
-rw-r--r--daemon/algod/api/server/v2/delta_test.go2
-rw-r--r--daemon/algod/api/server/v2/dryrun.go22
-rw-r--r--daemon/algod/api/server/v2/dryrun_test.go2
-rw-r--r--daemon/algod/api/server/v2/errors.go4
-rw-r--r--daemon/algod/api/server/v2/generated/data/data_routes.yml1
-rw-r--r--daemon/algod/api/server/v2/generated/data/routes.go340
-rw-r--r--daemon/algod/api/server/v2/generated/experimental/experimental_routes.yml21
-rw-r--r--daemon/algod/api/server/v2/generated/experimental/routes.go321
-rw-r--r--daemon/algod/api/server/v2/generated/model/types.go33
-rw-r--r--daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go338
-rw-r--r--daemon/algod/api/server/v2/generated/nonparticipating/public/public_routes.yml1
-rw-r--r--daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go451
-rw-r--r--daemon/algod/api/server/v2/generated/participating/private/routes.go342
-rw-r--r--daemon/algod/api/server/v2/generated/participating/public/public_routes.yml1
-rw-r--r--daemon/algod/api/server/v2/generated/participating/public/routes.go361
-rw-r--r--daemon/algod/api/server/v2/handlers.go134
-rw-r--r--daemon/algod/api/server/v2/handlers_test.go2
-rw-r--r--daemon/algod/api/server/v2/test/handlers_resources_test.go4
-rw-r--r--daemon/algod/api/server/v2/test/handlers_test.go150
-rw-r--r--daemon/algod/api/server/v2/test/helpers.go79
-rw-r--r--daemon/algod/api/server/v2/utils.go2
-rw-r--r--daemon/algod/api/spec/common/model.go2
-rw-r--r--daemon/algod/api/spec/v2/model.go2
-rw-r--r--daemon/algod/api/swagger.go2
-rw-r--r--daemon/algod/deadlockLogger.go2
-rw-r--r--daemon/algod/deadlock_test.go2
-rw-r--r--daemon/algod/server.go43
-rw-r--r--daemon/algod/server_test.go2
-rw-r--r--daemon/kmd/api/api.go2
-rw-r--r--daemon/kmd/api/cors.go2
-rw-r--r--daemon/kmd/api/v1/auth.go2
-rw-r--r--daemon/kmd/api/v1/errors.go2
-rw-r--r--daemon/kmd/api/v1/handlers.go2
-rw-r--r--daemon/kmd/client/client.go2
-rw-r--r--daemon/kmd/client/requests.go2
-rw-r--r--daemon/kmd/client/wrappers.go2
-rw-r--r--daemon/kmd/config/config.go2
-rw-r--r--daemon/kmd/config/errors.go2
-rw-r--r--daemon/kmd/kmd.go2
-rw-r--r--daemon/kmd/lib/kmdapi/common.go2
-rw-r--r--daemon/kmd/lib/kmdapi/requests.go2
-rw-r--r--daemon/kmd/lib/kmdapi/responses.go2
-rw-r--r--daemon/kmd/server/errors.go2
-rw-r--r--daemon/kmd/server/server.go2
-rw-r--r--daemon/kmd/session/auth.go2
-rw-r--r--daemon/kmd/session/session.go2
-rw-r--r--daemon/kmd/wallet/driver/driver.go2
-rw-r--r--daemon/kmd/wallet/driver/ledger.go2
-rw-r--r--daemon/kmd/wallet/driver/ledger_errors.go2
-rw-r--r--daemon/kmd/wallet/driver/ledger_hid.go2
-rw-r--r--daemon/kmd/wallet/driver/sqlite.go2
-rw-r--r--daemon/kmd/wallet/driver/sqlite_crypto.go2
-rw-r--r--daemon/kmd/wallet/driver/sqlite_errors.go2
-rw-r--r--daemon/kmd/wallet/driver/util.go2
-rw-r--r--daemon/kmd/wallet/wallet.go2
-rw-r--r--data/account/account.go2
-rw-r--r--data/account/partInstall.go2
-rw-r--r--data/account/participation.go2
-rw-r--r--data/account/participationRegistry.go2
-rw-r--r--data/account/participationRegistryBench_test.go2
-rw-r--r--data/account/participationRegistry_test.go18
-rw-r--r--data/account/participation_test.go4
-rw-r--r--data/account/registeryDbOps.go2
-rw-r--r--data/account/rootInstall.go2
-rw-r--r--data/accountManager.go2
-rw-r--r--data/accountManager_test.go13
-rw-r--r--data/basics/address.go2
-rw-r--r--data/basics/address_test.go2
-rw-r--r--data/basics/fields_test.go2
-rw-r--r--data/basics/overflow.go2
-rw-r--r--data/basics/sort.go2
-rw-r--r--data/basics/stateProofParticipant.go2
-rw-r--r--data/basics/teal.go2
-rw-r--r--data/basics/teal_test.go2
-rw-r--r--data/basics/testing/userBalance.go (renamed from logging/telemetryspec/operation.go)20
-rw-r--r--data/basics/units.go2
-rw-r--r--data/basics/units_test.go2
-rw-r--r--data/basics/userBalance.go7
-rw-r--r--data/basics/userBalance_test.go2
-rw-r--r--data/bookkeeping/block.go2
-rw-r--r--data/bookkeeping/block_test.go4
-rw-r--r--data/bookkeeping/encoding_test.go2
-rw-r--r--data/bookkeeping/genesis.go2
-rw-r--r--data/bookkeeping/genesis_test.go2
-rw-r--r--data/bookkeeping/lightBlockHeader.go2
-rw-r--r--data/bookkeeping/lightBlockHeader_test.go2
-rw-r--r--data/bookkeeping/prettyprinting.go2
-rw-r--r--data/bookkeeping/txn_merkle.go2
-rw-r--r--data/bookkeeping/txn_merkle_test.go2
-rw-r--r--data/committee/committee.go2
-rw-r--r--data/committee/common_test.go2
-rw-r--r--data/committee/credential.go2
-rw-r--r--data/committee/credential_test.go2
-rw-r--r--data/committee/encoding_test.go2
-rw-r--r--data/committee/sortition/sortition.go2
-rw-r--r--data/committee/sortition/sortition_test.go2
-rw-r--r--data/common_test.go9
-rw-r--r--data/datatest/fabricateLedger.go2
-rw-r--r--data/datatest/impls.go2
-rw-r--r--data/hashable/message.go2
-rw-r--r--data/ledger.go4
-rw-r--r--data/ledger_test.go19
-rw-r--r--data/pools/errors.go2
-rw-r--r--data/pools/statusCache.go2
-rw-r--r--data/pools/transactionPool.go2
-rw-r--r--data/pools/transactionPool_test.go2
-rw-r--r--data/stateproofmsg/message.go2
-rw-r--r--data/transactions/application.go2
-rw-r--r--data/transactions/application_test.go2
-rw-r--r--data/transactions/asset.go2
-rw-r--r--data/transactions/common_test.go2
-rw-r--r--data/transactions/error.go2
-rw-r--r--data/transactions/json_test.go8
-rw-r--r--data/transactions/keyreg.go2
-rw-r--r--data/transactions/logic/TEAL_opcodes.md10
-rw-r--r--data/transactions/logic/assembler.go28
-rw-r--r--data/transactions/logic/assembler_test.go112
-rw-r--r--data/transactions/logic/backwardCompat_test.go8
-rw-r--r--data/transactions/logic/blackbox_test.go6
-rw-r--r--data/transactions/logic/box.go36
-rw-r--r--data/transactions/logic/box_test.go65
-rw-r--r--data/transactions/logic/debugger.go112
-rw-r--r--data/transactions/logic/debugger_test.go86
-rw-r--r--data/transactions/logic/doc.go12
-rw-r--r--data/transactions/logic/doc_test.go11
-rw-r--r--data/transactions/logic/eval.go128
-rw-r--r--data/transactions/logic/evalAppTxn_test.go67
-rw-r--r--data/transactions/logic/evalBench_test.go2
-rw-r--r--data/transactions/logic/evalCrypto_test.go35
-rw-r--r--data/transactions/logic/evalStateful_test.go176
-rw-r--r--data/transactions/logic/eval_test.go215
-rw-r--r--data/transactions/logic/export_test.go2
-rw-r--r--data/transactions/logic/fields.go20
-rw-r--r--data/transactions/logic/fields_test.go2
-rw-r--r--data/transactions/logic/frames.go2
-rw-r--r--data/transactions/logic/frames_test.go2
-rw-r--r--data/transactions/logic/jsonspec_test.go19
-rw-r--r--data/transactions/logic/langspec.json41
-rw-r--r--data/transactions/logic/ledger_test.go2
-rw-r--r--data/transactions/logic/mocktracer/tracer.go147
-rw-r--r--data/transactions/logic/opcodes.go118
-rw-r--r--data/transactions/logic/opcodes_test.go5
-rw-r--r--data/transactions/logic/pairing.go2
-rw-r--r--data/transactions/logic/pairing_test.go2
-rw-r--r--data/transactions/logic/parsing.go105
-rw-r--r--data/transactions/logic/parsing_test.go139
-rw-r--r--data/transactions/logic/program.go2
-rw-r--r--data/transactions/logic/sourcemap.go2
-rw-r--r--data/transactions/logic/sourcemap_test.go4
-rw-r--r--data/transactions/logic/tracer.go160
-rw-r--r--data/transactions/logic/tracer_test.go195
-rw-r--r--data/transactions/logicsig.go2
-rw-r--r--data/transactions/payment.go2
-rw-r--r--data/transactions/payment_test.go2
-rw-r--r--data/transactions/payset.go2
-rw-r--r--data/transactions/payset_test.go2
-rw-r--r--data/transactions/perf_test.go2
-rw-r--r--data/transactions/signedtxn.go2
-rw-r--r--data/transactions/signedtxn_test.go2
-rw-r--r--data/transactions/sort.go2
-rw-r--r--data/transactions/stateproof.go2
-rw-r--r--data/transactions/teal.go2
-rw-r--r--data/transactions/teal_test.go2
-rw-r--r--data/transactions/testhelpers.go2
-rw-r--r--data/transactions/transaction.go2
-rw-r--r--data/transactions/transaction_test.go2
-rw-r--r--data/transactions/verify/artifact_test.go6
-rw-r--r--data/transactions/verify/txn.go490
-rw-r--r--data/transactions/verify/txn_test.go1108
-rw-r--r--data/transactions/verify/verifiedTxnCache.go10
-rw-r--r--data/transactions/verify/verifiedTxnCache_test.go24
-rw-r--r--data/txDupCache.go17
-rw-r--r--data/txDupCache_test.go10
-rw-r--r--data/txHandler.go175
-rw-r--r--data/txHandler_test.go1009
-rw-r--r--data/txntest/defi.go4
-rw-r--r--data/txntest/txn.go10
-rw-r--r--docker/README.md70
-rwxr-xr-xdocker/files/build/install.sh8
-rwxr-xr-xdocker/files/run/run.sh43
-rw-r--r--gen/generate.go2
-rw-r--r--gen/generate_test.go2
-rw-r--r--gen/walletData.go2
-rw-r--r--go.mod2
-rw-r--r--go.sum6
-rw-r--r--installer/config.json.example19
-rw-r--r--ledger/acctdeltas.go (renamed from ledger/accountdb.go)614
-rw-r--r--ledger/acctdeltas_test.go (renamed from ledger/accountdb_test.go)14
-rw-r--r--ledger/acctonline.go6
-rw-r--r--ledger/acctonline_test.go2
-rw-r--r--ledger/acctupdates.go2
-rw-r--r--ledger/acctupdates_test.go14
-rw-r--r--ledger/applications_test.go2
-rw-r--r--ledger/apply/application.go2
-rw-r--r--ledger/apply/application_test.go2
-rw-r--r--ledger/apply/apply.go2
-rw-r--r--ledger/apply/asset.go2
-rw-r--r--ledger/apply/asset_test.go2
-rw-r--r--ledger/apply/keyreg.go2
-rw-r--r--ledger/apply/keyreg_test.go2
-rw-r--r--ledger/apply/mockBalances_test.go2
-rw-r--r--ledger/apply/payment.go2
-rw-r--r--ledger/apply/payment_test.go2
-rw-r--r--ledger/apply/stateproof.go2
-rw-r--r--ledger/apptxn_test.go2
-rw-r--r--ledger/archival_test.go32
-rw-r--r--ledger/blockHeaderCache.go2
-rw-r--r--ledger/blockHeaderCache_test.go2
-rw-r--r--ledger/blockqueue.go52
-rw-r--r--ledger/blockqueue_test.go2
-rw-r--r--ledger/boxtxn_test.go2
-rw-r--r--ledger/bulletin.go7
-rw-r--r--ledger/bulletin_test.go5
-rw-r--r--ledger/catchpointfileheader.go2
-rw-r--r--ledger/catchpointtracker.go18
-rw-r--r--ledger/catchpointtracker_test.go2
-rw-r--r--ledger/catchpointwriter.go81
-rw-r--r--ledger/catchpointwriter_test.go60
-rw-r--r--ledger/catchupaccessor.go19
-rw-r--r--ledger/catchupaccessor_test.go21
-rw-r--r--ledger/double_test.go6
-rw-r--r--ledger/encoded/msgp_gen.go587
-rw-r--r--ledger/encoded/msgp_gen_test.go195
-rw-r--r--ledger/encoded/recordsV5.go (renamed from node/poolStats.go)20
-rw-r--r--ledger/encoded/recordsV6.go64
-rw-r--r--ledger/encoded/recordsV6_test.go63
-rw-r--r--ledger/eval_simple_test.go2
-rw-r--r--ledger/evalbench_test.go2
-rw-r--r--ledger/evalindexer.go2
-rw-r--r--ledger/evalindexer_test.go2
-rw-r--r--ledger/fullblock_perf_test.go5
-rw-r--r--ledger/internal/appcow.go2
-rw-r--r--ledger/internal/appcow_test.go2
-rw-r--r--ledger/internal/applications.go11
-rw-r--r--ledger/internal/assetcow.go2
-rw-r--r--ledger/internal/cow.go2
-rw-r--r--ledger/internal/cow_creatables.go2
-rw-r--r--ledger/internal/cow_test.go2
-rw-r--r--ledger/internal/eval.go30
-rw-r--r--ledger/internal/eval_test.go202
-rw-r--r--ledger/internal/evalindexer.go2
-rw-r--r--ledger/internal/prefetcher/error.go2
-rw-r--r--ledger/internal/prefetcher/prefetcher.go2
-rw-r--r--ledger/internal/prefetcher/prefetcher_alignment_test.go2
-rw-r--r--ledger/internal/prefetcher/prefetcher_test.go2
-rw-r--r--ledger/internal/prefetcher/prefetcher_whitebox_test.go2
-rw-r--r--ledger/ledger.go25
-rw-r--r--ledger/ledger_perf_test.go7
-rw-r--r--ledger/ledger_test.go58
-rw-r--r--ledger/ledgercore/accountdata.go2
-rw-r--r--ledger/ledgercore/accountdata_test.go2
-rw-r--r--ledger/ledgercore/accountresource.go2
-rw-r--r--ledger/ledgercore/catchpointlabel.go2
-rw-r--r--ledger/ledgercore/catchpointlabel_test.go2
-rw-r--r--ledger/ledgercore/error.go2
-rw-r--r--ledger/ledgercore/misc.go7
-rw-r--r--ledger/ledgercore/onlineacct.go2
-rw-r--r--ledger/ledgercore/statedelta.go2
-rw-r--r--ledger/ledgercore/statedelta_test.go2
-rw-r--r--ledger/ledgercore/totals.go2
-rw-r--r--ledger/ledgercore/totals_test.go2
-rw-r--r--ledger/ledgercore/validatedBlock.go2
-rw-r--r--ledger/ledgercore/votersForRound.go2
-rw-r--r--ledger/lruaccts.go2
-rw-r--r--ledger/lruaccts_test.go2
-rw-r--r--ledger/lrukv.go2
-rw-r--r--ledger/lrukv_test.go2
-rw-r--r--ledger/lruonlineaccts.go2
-rw-r--r--ledger/lruonlineaccts_test.go2
-rw-r--r--ledger/lruresources.go2
-rw-r--r--ledger/lruresources_test.go2
-rw-r--r--ledger/metrics.go8
-rw-r--r--ledger/metrics_test.go2
-rw-r--r--ledger/msgp_gen.go1060
-rw-r--r--ledger/msgp_gen_test.go180
-rw-r--r--ledger/notifier.go11
-rw-r--r--ledger/onlineaccountscache.go2
-rw-r--r--ledger/onlineaccountscache_test.go2
-rw-r--r--ledger/onlinetopheap.go2
-rw-r--r--ledger/onlinetopheap_test.go2
-rw-r--r--ledger/perf_test.go2
-rw-r--r--ledger/persistedaccts_list.go2
-rw-r--r--ledger/persistedaccts_list_test.go2
-rw-r--r--ledger/persistedkvs.go2
-rw-r--r--ledger/persistedkvs_test.go2
-rw-r--r--ledger/persistedonlineaccts_list.go2
-rw-r--r--ledger/persistedonlineaccts_list_test.go2
-rw-r--r--ledger/persistedresources_list.go2
-rw-r--r--ledger/persistedresources_list_test.go2
-rw-r--r--ledger/roundlru.go2
-rw-r--r--ledger/roundlru_test.go2
-rw-r--r--ledger/simple_test.go4
-rw-r--r--ledger/simulation/simulator.go217
-rw-r--r--ledger/simulation/simulator_test.go746
-rw-r--r--ledger/store/accountsV2.go2
-rw-r--r--ledger/store/accountsV2_test.go2
-rw-r--r--ledger/store/blockdb/blockdb.go2
-rw-r--r--ledger/store/blockdb/blockdb_test.go2
-rw-r--r--ledger/store/catchpoint.go2
-rw-r--r--ledger/store/catchpointPendingHashesIter.go81
-rw-r--r--ledger/store/catchpoint_test.go2
-rw-r--r--ledger/store/data.go2
-rw-r--r--ledger/store/data_test.go2
-rw-r--r--ledger/store/encodedAccountsIter.go165
-rw-r--r--ledger/store/hashing.go2
-rw-r--r--ledger/store/interface.go2
-rw-r--r--ledger/store/kvsIter.go53
-rw-r--r--ledger/store/merkle_commiter.go2
-rw-r--r--ledger/store/orderedAccountsIter.go433
-rw-r--r--ledger/store/schema.go2
-rw-r--r--ledger/store/schema_test.go2
-rw-r--r--ledger/store/sql.go2
-rw-r--r--ledger/store/sql_test.go2
-rw-r--r--ledger/store/testing.go2
-rw-r--r--ledger/store/testing/helpers.go2
-rw-r--r--ledger/store/trackerdbV2.go2
-rw-r--r--ledger/testing/accountsTotals.go2
-rw-r--r--ledger/testing/consensusRange.go2
-rw-r--r--ledger/testing/consensusRange_test.go2
-rw-r--r--ledger/testing/initState.go9
-rw-r--r--ledger/testing/randomAccounts.go12
-rw-r--r--ledger/testing/randomAccounts_test.go2
-rw-r--r--ledger/testing/testGenesis.go2
-rw-r--r--ledger/tracker.go2
-rw-r--r--ledger/tracker_test.go2
-rw-r--r--ledger/trackerdb.go2
-rw-r--r--ledger/txnbench_test.go2
-rw-r--r--ledger/txtail.go2
-rw-r--r--ledger/txtail_test.go2
-rw-r--r--ledger/voters.go5
-rw-r--r--ledger/voters_test.go2
-rw-r--r--libgoal/accounts.go2
-rw-r--r--libgoal/error.go2
-rw-r--r--libgoal/libgoal.go4
-rw-r--r--libgoal/libgoal_test.go2
-rw-r--r--libgoal/lockedFile.go2
-rw-r--r--libgoal/lockedFileLinux.go2
-rw-r--r--libgoal/lockedFileUnix.go2
-rw-r--r--libgoal/lockedFileWindows.go2
-rw-r--r--libgoal/participation.go2
-rw-r--r--libgoal/system.go2
-rw-r--r--libgoal/teal.go2
-rw-r--r--libgoal/transactions.go2
-rw-r--r--libgoal/unencryptedWallet.go2
-rw-r--r--libgoal/walletHandles.go2
-rw-r--r--libgoal/wallets.go2
-rw-r--r--logging/collector.go2
-rw-r--r--logging/cyclicWriter.go2
-rw-r--r--logging/cyclicWriter_test.go2
-rw-r--r--logging/log.go15
-rw-r--r--logging/logBuffer.go2
-rw-r--r--logging/logBuffer_test.go2
-rw-r--r--logging/log_test.go11
-rw-r--r--logging/logspec/agreement.go2
-rw-r--r--logging/logspec/ledger.go2
-rw-r--r--logging/logspec/root.go2
-rw-r--r--logging/telemetry.go8
-rw-r--r--logging/telemetryCommon.go17
-rw-r--r--logging/telemetryConfig.go2
-rw-r--r--logging/telemetryConfig_test.go2
-rw-r--r--logging/telemetryFilteredHook.go2
-rw-r--r--logging/telemetryOperation.go51
-rw-r--r--logging/telemetry_test.go33
-rw-r--r--logging/telemetryhook.go6
-rw-r--r--logging/telemetryhook_test.go2
-rw-r--r--logging/telemetryspec/category.go2
-rw-r--r--logging/telemetryspec/event.go13
-rw-r--r--logging/telemetryspec/eventTiming.go2
-rw-r--r--logging/telemetryspec/metric.go61
-rw-r--r--logging/telemetryspec/metric_test.go24
-rw-r--r--logging/testingLogger.go2
-rw-r--r--logging/usage.go4
-rw-r--r--netdeploy/network.go2
-rw-r--r--netdeploy/networkTemplate.go2
-rw-r--r--netdeploy/networkTemplates_test.go2
-rw-r--r--netdeploy/network_test.go2
-rw-r--r--netdeploy/remote/bootstrappedNetwork.go2
-rw-r--r--netdeploy/remote/bootstrappedNetwork_test.go2
-rw-r--r--netdeploy/remote/buildConfig.go2
-rw-r--r--netdeploy/remote/deployedNetwork.go2
-rw-r--r--netdeploy/remote/deployedNetwork_test.go2
-rw-r--r--netdeploy/remote/hostConfig.go2
-rw-r--r--netdeploy/remote/hostTemplate.go2
-rw-r--r--netdeploy/remote/nodeConfig.go2
-rw-r--r--netdeploy/remote/nodeWalletData.go2
-rw-r--r--netdeploy/remote/nodecfg/nodeConfigurator.go6
-rw-r--r--netdeploy/remote/nodecfg/nodeDir.go4
-rw-r--r--netdeploy/remote/topology.go2
-rw-r--r--network/connPerfMon.go2
-rw-r--r--network/connPerfMon_test.go2
-rw-r--r--network/dialer.go2
-rw-r--r--network/limited_reader_slurper.go2
-rw-r--r--network/limited_reader_slurper_test.go2
-rw-r--r--network/messageFilter.go2
-rw-r--r--network/messageFilter_test.go2
-rw-r--r--network/messagetracer/graphtrace.go2
-rw-r--r--network/messagetracer/interface.go2
-rw-r--r--network/msgCompressor.go2
-rw-r--r--network/msgCompressor_test.go2
-rw-r--r--network/msgOfInterest.go2
-rw-r--r--network/msgOfInterest_test.go2
-rw-r--r--network/multiplexer.go2
-rw-r--r--network/multiplexer_test.go2
-rw-r--r--network/netprio.go2
-rw-r--r--network/netprio_test.go2
-rw-r--r--network/peersheap.go2
-rw-r--r--network/phonebook.go2
-rw-r--r--network/phonebook_test.go2
-rw-r--r--network/rateLimitingTransport.go2
-rw-r--r--network/requestLogger.go2
-rw-r--r--network/requestLogger_test.go2
-rw-r--r--network/requestTracker.go2
-rw-r--r--network/requestTracker_test.go2
-rw-r--r--network/topics.go2
-rw-r--r--network/topics_test.go2
-rw-r--r--network/wsNetwork.go24
-rw-r--r--network/wsNetwork_test.go2
-rw-r--r--network/wsPeer.go19
-rw-r--r--network/wsPeer_test.go2
-rw-r--r--node/assemble_test.go2
-rw-r--r--node/error.go2
-rw-r--r--node/impls.go2
-rw-r--r--node/indexer/db.go2
-rw-r--r--node/indexer/indexer.go2
-rw-r--r--node/indexer/indexer_test.go2
-rw-r--r--node/netprio.go2
-rw-r--r--node/node.go66
-rw-r--r--node/node_test.go9
-rw-r--r--node/topAccountListener.go212
-rw-r--r--node/topAccountListener_test.go368
-rw-r--r--nodecontrol/LaggedStdIo.go2
-rw-r--r--nodecontrol/NodeController.go2
-rw-r--r--nodecontrol/algodControl.go2
-rw-r--r--nodecontrol/algodControl_test.go2
-rw-r--r--nodecontrol/kmdControl.go2
-rw-r--r--nodecontrol/kmdControl_common.go2
-rw-r--r--nodecontrol/kmdControl_windows.go2
-rw-r--r--nodecontrol/nodeControlErrors.go2
-rw-r--r--protocol/codec.go2
-rw-r--r--protocol/codec_test.go2
-rw-r--r--protocol/codec_tester.go2
-rw-r--r--protocol/consensus.go2
-rw-r--r--protocol/encodebench_test.go2
-rw-r--r--protocol/hash.go2
-rw-r--r--protocol/networks.go2
-rw-r--r--protocol/stateproof.go2
-rw-r--r--protocol/tags.go2
-rw-r--r--protocol/test/allocbound_slice.go2
-rw-r--r--protocol/transcode/core.go2
-rw-r--r--protocol/transcode/core_test.go2
-rw-r--r--protocol/txntype.go2
-rw-r--r--rpcs/blockService.go2
-rw-r--r--rpcs/blockService_test.go2
-rw-r--r--rpcs/httpTxSync.go2
-rw-r--r--rpcs/ledgerService.go2
-rw-r--r--rpcs/registrar.go2
-rw-r--r--rpcs/txService.go2
-rw-r--r--rpcs/txService_test.go2
-rw-r--r--rpcs/txSyncer.go2
-rw-r--r--rpcs/txSyncer_test.go2
-rwxr-xr-xscripts/compute_branch.sh29
-rw-r--r--shared/algoh/config.go2
-rw-r--r--shared/pingpong/accounts.go2
-rw-r--r--shared/pingpong/accounts_test.go7
-rw-r--r--shared/pingpong/config.go3
-rw-r--r--shared/pingpong/pingpong.go196
-rw-r--r--stateproof/abstractions.go2
-rw-r--r--stateproof/builder.go2
-rw-r--r--stateproof/db.go2
-rw-r--r--stateproof/db_test.go2
-rw-r--r--stateproof/recovery.go2
-rw-r--r--stateproof/signer.go2
-rw-r--r--stateproof/stateproofMessageGenerator.go2
-rw-r--r--stateproof/stateproofMessageGenerator_test.go2
-rw-r--r--stateproof/verify/stateproof.go2
-rw-r--r--stateproof/verify/stateproof_test.go2
-rw-r--r--stateproof/worker.go2
-rw-r--r--stateproof/worker_test.go2
-rw-r--r--test/commandandcontrol/cc_agent/component/agent.go2
-rw-r--r--test/commandandcontrol/cc_agent/component/agent_test.go2
-rw-r--r--test/commandandcontrol/cc_agent/component/pingPongComponent.go2
-rw-r--r--test/commandandcontrol/cc_agent/main.go2
-rw-r--r--test/commandandcontrol/cc_client/main.go2
-rw-r--r--test/commandandcontrol/cc_service/main.go2
-rw-r--r--test/commandandcontrol/lib/ccCommon.go2
-rw-r--r--test/e2e-go/cli/algod/cleanup_test.go2
-rw-r--r--test/e2e-go/cli/algod/expect/algod_expect_test.go2
-rw-r--r--test/e2e-go/cli/algod/stdstreams_test.go2
-rw-r--r--test/e2e-go/cli/algoh/expect/algoh_expect_test.go2
-rw-r--r--test/e2e-go/cli/goal/account_test.go2
-rw-r--r--test/e2e-go/cli/goal/clerk_test.go2
-rw-r--r--test/e2e-go/cli/goal/common_test.go2
-rw-r--r--test/e2e-go/cli/goal/expect/catchpointCatchupWebProxy/webproxy.go2
-rw-r--r--test/e2e-go/cli/goal/expect/goal_expect_test.go2
-rw-r--r--test/e2e-go/cli/goal/node_cleanup_test.go2
-rw-r--r--test/e2e-go/cli/perf/libgoal_test.go2
-rw-r--r--test/e2e-go/cli/perf/payment_test.go2
-rw-r--r--test/e2e-go/cli/tealdbg/cdtmock/main.go2
-rw-r--r--test/e2e-go/cli/tealdbg/expect/tealdbg_expect_test.go2
-rw-r--r--test/e2e-go/features/accountPerf/sixMillion_test.go2
-rw-r--r--test/e2e-go/features/catchup/basicCatchup_test.go2
-rw-r--r--test/e2e-go/features/catchup/catchpointCatchup_test.go2
-rw-r--r--test/e2e-go/features/devmode/devmode_test.go2
-rw-r--r--test/e2e-go/features/multisig/multisig_test.go2
-rw-r--r--test/e2e-go/features/participation/accountParticipationTransitions_test.go2
-rw-r--r--test/e2e-go/features/participation/deletePartKeys_test.go2
-rw-r--r--test/e2e-go/features/participation/onlineOfflineParticipation_test.go2
-rw-r--r--test/e2e-go/features/participation/overlappingParticipationKeys_test.go2
-rw-r--r--test/e2e-go/features/participation/participationExpiration_test.go2
-rw-r--r--test/e2e-go/features/participation/participationRewards_test.go2
-rw-r--r--test/e2e-go/features/partitionRecovery/partitionRecovery_test.go2
-rw-r--r--test/e2e-go/features/stateproofs/stateproofs_test.go2
-rw-r--r--test/e2e-go/features/teal/compile_test.go2
-rw-r--r--test/e2e-go/features/transactions/accountv2_test.go2
-rw-r--r--test/e2e-go/features/transactions/app_pages_test.go2
-rw-r--r--test/e2e-go/features/transactions/application_test.go2
-rw-r--r--test/e2e-go/features/transactions/asset_test.go2
-rw-r--r--test/e2e-go/features/transactions/close_account_test.go2
-rw-r--r--test/e2e-go/features/transactions/group_test.go2
-rw-r--r--test/e2e-go/features/transactions/lease_test.go2
-rw-r--r--test/e2e-go/features/transactions/onlineStatusChange_test.go2
-rw-r--r--test/e2e-go/features/transactions/proof_test.go2
-rw-r--r--test/e2e-go/features/transactions/sendReceive_test.go2
-rw-r--r--test/e2e-go/globals/constants.go2
-rw-r--r--test/e2e-go/kmd/e2e_kmd_server_client_test.go2
-rw-r--r--test/e2e-go/kmd/e2e_kmd_sqlite_test.go2
-rw-r--r--test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go2
-rw-r--r--test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go2
-rw-r--r--test/e2e-go/kmd/e2e_kmd_wallet_test.go2
-rw-r--r--test/e2e-go/perf/basic_test.go2
-rw-r--r--test/e2e-go/restAPI/restClient_test.go2
-rw-r--r--test/e2e-go/stress/transactions/createManyAndGoOnline_test.go2
-rw-r--r--test/e2e-go/upgrades/application_support_test.go2
-rw-r--r--test/e2e-go/upgrades/rekey_support_test.go2
-rw-r--r--test/e2e-go/upgrades/send_receive_upgrade_test.go2
-rw-r--r--test/e2e-go/upgrades/stateproof_participation_test.go2
-rw-r--r--test/framework/fixtures/baseFixture.go2
-rw-r--r--test/framework/fixtures/expectFixture.go2
-rw-r--r--test/framework/fixtures/fixture.go2
-rw-r--r--test/framework/fixtures/goalFixture.go2
-rw-r--r--test/framework/fixtures/kmdFixture.go2
-rw-r--r--test/framework/fixtures/libgoalFixture.go2
-rw-r--r--test/framework/fixtures/restClientFixture.go2
-rw-r--r--test/framework/fixtures/webProxyFixture.go2
-rw-r--r--test/heapwatch/block_history.py9
-rw-r--r--test/heapwatch/block_history_plot.py10
-rw-r--r--test/heapwatch/block_history_relays.py2
-rw-r--r--test/heapwatch/client_ram_report.py2
-rw-r--r--test/heapwatch/heapWatch.py2
-rw-r--r--test/heapwatch/metrics_delta.py65
-rw-r--r--test/heapwatch/nodeHostTarget.py2
-rwxr-xr-xtest/heapwatch/plot_crr_csv.py3
-rw-r--r--test/heapwatch/runNodeHost.py2
-rw-r--r--test/linttest/lintissues.go2
-rw-r--r--test/netperf-go/puppeteer/main.go2
-rw-r--r--test/netperf-go/puppeteer/promMetricFetcher.go2
-rw-r--r--test/netperf-go/puppeteer/promMetricFetcher_test.go2
-rw-r--r--test/netperf-go/puppeteer/puppeteer.go2
-rw-r--r--test/netperf-go/puppeteer/puppeteer_test.go2
-rw-r--r--test/netperf-go/puppeteer/roundpoller.go2
-rw-r--r--test/partitiontest/filtering.go2
-rw-r--r--test/reflectionhelpers/helpers.go2
-rwxr-xr-xtest/scripts/e2e.sh13
-rwxr-xr-xtest/scripts/e2e_basic_start_stop.sh63
-rwxr-xr-xtest/scripts/e2e_subs/e2e-app-simple.sh24
-rw-r--r--test/scripts/e2e_tx_latency.py400
-rw-r--r--test/scripts/latency_plot.py117
-rw-r--r--test/testdata/configs/config-v26.json3
-rw-r--r--test/testdata/configs/config-v27.json116
-rw-r--r--tools/boxkey/convertBoxKey.go48
-rw-r--r--tools/debug/algodump/main.go2
-rw-r--r--tools/debug/carpenter/main.go2
-rw-r--r--tools/debug/coroner/main.go2
-rw-r--r--tools/debug/determaccount/main.go2
-rw-r--r--tools/debug/dumpblocks/main.go2
-rw-r--r--tools/debug/genconsensusconfig/main.go2
-rw-r--r--tools/debug/logfilter/main.go2
-rw-r--r--tools/debug/logfilter/main_test.go2
-rw-r--r--tools/misc/convertAddress.go2
-rw-r--r--tools/network/bootstrap.go2
-rw-r--r--tools/network/cloudflare/cloudflare.go2
-rw-r--r--tools/network/cloudflare/createRecord.go2
-rw-r--r--tools/network/cloudflare/deleteRecord.go2
-rw-r--r--tools/network/cloudflare/helpers.go2
-rw-r--r--tools/network/cloudflare/listRecords.go2
-rw-r--r--tools/network/cloudflare/updateRecord.go2
-rw-r--r--tools/network/cloudflare/zones.go2
-rw-r--r--tools/network/dnssec/anchor.go2
-rw-r--r--tools/network/dnssec/anchor_test.go2
-rw-r--r--tools/network/dnssec/client.go2
-rw-r--r--tools/network/dnssec/client_test.go2
-rw-r--r--tools/network/dnssec/config.go2
-rw-r--r--tools/network/dnssec/config_test.go2
-rw-r--r--tools/network/dnssec/config_unix.go2
-rw-r--r--tools/network/dnssec/config_unix_test.go2
-rw-r--r--tools/network/dnssec/config_windows.go2
-rw-r--r--tools/network/dnssec/dialer.go2
-rw-r--r--tools/network/dnssec/dnssec_test.go2
-rw-r--r--tools/network/dnssec/relay-check/main.go2
-rw-r--r--tools/network/dnssec/resolver.go2
-rw-r--r--tools/network/dnssec/sort.go2
-rw-r--r--tools/network/dnssec/sort_test.go2
-rw-r--r--tools/network/dnssec/testHarness.go2
-rw-r--r--tools/network/dnssec/trustchain.go2
-rw-r--r--tools/network/dnssec/trustedchain_test.go2
-rw-r--r--tools/network/dnssec/trustedzone.go2
-rw-r--r--tools/network/dnssec/trustedzone_test.go2
-rw-r--r--tools/network/dnssec/util.go2
-rw-r--r--tools/network/dnssec/util_test.go2
-rw-r--r--tools/network/externalIP.go2
-rw-r--r--tools/network/resolveController.go2
-rw-r--r--tools/network/resolveController_test.go2
-rw-r--r--tools/network/resolver.go2
-rw-r--r--tools/network/resolver_test.go38
-rw-r--r--tools/network/telemetryURIUpdateService.go2
-rw-r--r--tools/network/telemetryURIUpdateService_test.go2
-rw-r--r--tools/teal/algotmpl/extract.go2
-rw-r--r--tools/teal/algotmpl/main.go2
-rw-r--r--tools/teal/dkey/dsign/main.go2
-rw-r--r--tools/teal/tealcut/main.go2
-rw-r--r--util/cmdUtils.go2
-rw-r--r--util/codecs/json.go2
-rw-r--r--util/codecs/json_test.go2
-rw-r--r--util/condvar/timedwait.go2
-rw-r--r--util/condvar/timedwait_test.go2
-rw-r--r--util/db/dbpair.go2
-rw-r--r--util/db/dbutil.go2
-rw-r--r--util/db/dbutil_test.go2
-rw-r--r--util/db/fullfsync_darwin.go2
-rw-r--r--util/db/initialize.go2
-rw-r--r--util/db/initialize_test.go2
-rw-r--r--util/db/interfaces.go2
-rw-r--r--util/db/perf_test.go2
-rw-r--r--util/db/versioning.go2
-rw-r--r--util/db/versioning_test.go2
-rw-r--r--util/execpool/backlog.go8
-rw-r--r--util/execpool/pool.go2
-rw-r--r--util/io.go2
-rw-r--r--util/io_test.go2
-rw-r--r--util/metrics/counter.go24
-rw-r--r--util/metrics/counterCommon.go4
-rw-r--r--util/metrics/counter_test.go12
-rw-r--r--util/metrics/gauge.go31
-rw-r--r--util/metrics/gauge_test.go6
-rw-r--r--util/metrics/metrics.go4
-rw-r--r--util/metrics/metrics_test.go2
-rw-r--r--util/metrics/registry.go2
-rw-r--r--util/metrics/registryCommon.go2
-rw-r--r--util/metrics/registry_test.go10
-rw-r--r--util/metrics/reporter.go2
-rwxr-xr-xutil/metrics/reporter_test.go2
-rw-r--r--util/metrics/runtime.go2
-rw-r--r--util/metrics/runtime_test.go2
-rw-r--r--util/metrics/service.go2
-rw-r--r--util/metrics/serviceCommon.go2
-rw-r--r--util/metrics/tagcounter.go2
-rw-r--r--util/metrics/tagcounter_test.go2
-rw-r--r--util/process.go2
-rw-r--r--util/process_common.go2
-rw-r--r--util/process_windows.go2
-rw-r--r--util/rateLimit.go556
-rw-r--r--util/rateLimit_test.go291
-rw-r--r--util/s3/fileIterator.go2
-rw-r--r--util/s3/s3Helper.go106
-rw-r--r--util/s3/s3Helper_test.go36
-rw-r--r--util/sleep.go2
-rw-r--r--util/sleep_linux.go2
-rw-r--r--util/sleep_linux_32.go2
-rw-r--r--util/sleep_linux_64.go2
-rw-r--r--util/tar/tar.go2
-rw-r--r--util/tar/untar.go2
-rw-r--r--util/tcpinfo.go2
-rw-r--r--util/tcpinfo_darwin.go2
-rw-r--r--util/tcpinfo_linux.go3
-rw-r--r--util/tcpinfo_noop.go2
-rw-r--r--util/timers/frozen.go2
-rw-r--r--util/timers/interface.go2
-rw-r--r--util/timers/monotonic.go2
-rw-r--r--util/timers/monotonic_test.go2
-rw-r--r--util/tokens/tokens.go2
-rw-r--r--util/util.go12
-rw-r--r--util/util_windows.go8
-rw-r--r--util/uuid/uuid.go2
-rw-r--r--util/uuid/uuid_test.go2
-rw-r--r--util/watchdogStreamReader.go2
1064 files changed, 13065 insertions, 6875 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index a4567113a..7842cb703 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -439,6 +439,7 @@ commands:
- run:
working_directory: /tmp
command: |
+ sudo rm -rf ${HOME}/node_pkg/*
sudo rm -rf << parameters.build_dir >>
sudo mkdir -p << parameters.build_dir >>
sudo chown -R $USER:$GROUP << parameters.build_dir >>
diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml
new file mode 100644
index 000000000..fa4b39c4c
--- /dev/null
+++ b/.github/workflows/container.yml
@@ -0,0 +1,57 @@
+name: container
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - master
+ - rel/*
+ - feature/*
+ tags:
+ - "*"
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v3
+
+ - name: Generate Container Metadata
+ id: meta
+ uses: docker/metadata-action@v4
+ with:
+ images: |
+ docker.io/${{ github.repository_owner }}/algod
+ tags: |
+ type=sha,format=long,prefix=
+ type=ref,event=tag
+ type=ref,event=branch
+ type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'rel/stable') }}
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Setup QEMU
+ uses: docker/setup-qemu-action@v2
+
+ - name: Setup Docker Buildx
+ uses: docker/setup-buildx-action@v2
+
+ - name: Build and Push
+ uses: docker/build-push-action@v3
+ with:
+ context: ./
+ file: ./Dockerfile
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ platforms: linux/amd64,linux/arm64
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+ build-args: |
+ SHA=${{ github.sha }}
+ URL=${{ github.server_url }}/${{ github.repository }}.git
+ BRANCH=${{ github.ref_name }}
diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml
index d76ac49e8..ec1a85943 100644
--- a/.github/workflows/reviewdog.yml
+++ b/.github/workflows/reviewdog.yml
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 0 # required for new-from-rev option in .golangci.yml
- name: Install libraries
@@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 0 # required for new-from-rev option in .golangci.yml
- name: Install libraries
@@ -46,14 +46,14 @@ jobs:
echo "$GITHUB_WORKSPACE/bin" >> $GITHUB_PATH
echo "$RUNNER_WORKSPACE/$(basename $GITHUB_REPOSITORY)/bin" >> $GITHUB_PATH
- name: Install specific golang
- uses: actions/setup-go@v2
+ uses: actions/setup-go@v3
with:
go-version: '1.17.13'
- name: Create folders for golangci-lint
run: mkdir -p cicdtmp/golangci-lint
- name: Check if custom golangci-lint is already built
id: cache-golangci-lint
- uses: actions/cache@v2
+ uses: actions/cache@v3
with:
path: cicdtmp/golangci-lint/golangci-lint-cgo
key: cicd-golangci-lint-cgo-v0.0.1
@@ -69,7 +69,7 @@ jobs:
cd ../../
- name: Install reviewdog
run: |
- curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/v0.13.0/install.sh | sh -s
+ curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/v0.14.1/install.sh | sh -s
reviewdog --version
- name: Build custom linters
run: |
@@ -80,21 +80,25 @@ jobs:
- name: Run golangci-lint with reviewdog
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: >
- ./cicdtmp/golangci-lint/golangci-lint-cgo run
- --out-format line-number
- -c .golangci-warnings.yml
- --allow-parallel-runners
- | reviewdog
- -f=golangci-lint
- -name="Lint Warnings"
- -reporter=github-check
- -filter-mode=added
- -fail-on-error=false
+ run: |
+ set -e
+
+ ./cicdtmp/golangci-lint/golangci-lint-cgo run \
+ --out-format line-number \
+ -c .golangci-warnings.yml \
+ --issues-exit-code 0 \
+ --allow-parallel-runners > temp_golangci-lint-cgo.txt
+
+ cat temp_golangci-lint-cgo.txt | reviewdog \
+ -f=golangci-lint \
+ -name="Lint Warnings" \
+ -reporter=github-check \
+ -filter-mode=added \
+ -fail-on-error=true \
-level=warning
- name: Slack Notification
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
run: |
curl -X POST --data-urlencode "payload={\"text\": \"Reviewdog failed. ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} \"}" $SLACK_WEBHOOK
- if: ${{ failure() && (contains(github.ref_name, 'rel/nightly') || contains(github.ref_name, 'rel/beta') || contains(github.ref_name, 'rel/stable') || contains(github.ref_name, 'master')) }} \ No newline at end of file
+ if: ${{ failure() && (contains(github.ref_name, 'rel/nightly') || contains(github.ref_name, 'rel/beta') || contains(github.ref_name, 'rel/stable') || contains(github.ref_name, 'master')) }}
diff --git a/.golangci-warnings.yml b/.golangci-warnings.yml
index e3b8d22ff..f8d206347 100644
--- a/.golangci-warnings.yml
+++ b/.golangci-warnings.yml
@@ -9,10 +9,8 @@ linters:
- partitiontest
- structcheck
- varcheck
- - unconvert
- unused
-
linters-settings:
custom:
partitiontest:
@@ -55,7 +53,6 @@ issues:
- deadcode
- structcheck
- varcheck
- - unconvert
- unused
# Add all linters here -- Comment this block out for testing linters
- path: test/linttest/lintissues\.go
@@ -63,7 +60,6 @@ issues:
- deadcode
- structcheck
- varcheck
- - unconvert
- unused
- path: crypto/secp256k1/secp256_test\.go
linters:
diff --git a/.golangci.yml b/.golangci.yml
index 0122edf82..30b4d2812 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -117,7 +117,61 @@ issues:
- staticcheck
- typecheck
# Ignore missing parallel tests in existing packages
- - path: (agreement|catchup|cmd|config|crypto|daemon|data|gen|ledger|logging|netdeploy|network|node|protocol|rpcs|shared|stateproof|test|tools|util).*_test.go
+ - path: agreement.*_test\.go
+ linters:
+ - paralleltest
+ - path: catchup.*_test\.go
+ linters:
+ - paralleltest
+ - path: cmd.*_test\.go
+ linters:
+ - paralleltest
+ - path: config.*_test\.go
+ linters:
+ - paralleltest
+ - path: crypto.*_test\.go
+ linters:
+ - paralleltest
+ - path: daemon.*_test\.go
+ linters:
+ - paralleltest
+ - path: data.*_test\.go
+ linters:
+ - paralleltest
+ - path: gen.*_test\.go
+ linters:
+ - paralleltest
+ - path: ledger.*_test\.go
+ linters:
+ - paralleltest
+ - path: logging.*_test\.go
+ linters:
+ - paralleltest
+ - path: netdeploy.*_test\.go
+ linters:
+ - paralleltest
+ - path: network.*_test\.go
+ linters:
+ - paralleltest
+ - path: node.*_test\.go
+ linters:
+ - paralleltest
+ - path: protocol.*_test\.go
+ linters:
+ - paralleltest
+ - path: rpcs.*_test\.go
+ linters:
+ - paralleltest
+ - path: stateproof.*_test\.go
+ linters:
+ - paralleltest
+ - path: test.*_test\.go
+ linters:
+ - paralleltest
+ - path: tools.*_test\.go
+ linters:
+ - paralleltest
+ - path: util.*_test\.go
linters:
- paralleltest
# Add all linters here -- Comment this block out for testing linters
diff --git a/Dockerfile b/Dockerfile
index 56c0a8b79..ab55a9094 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,75 +1,66 @@
-ARG GO_VERSION=1.17.5
-FROM golang:$GO_VERSION-bullseye as builder
+FROM ubuntu:18.04 as builder
-ARG CHANNEL=nightly
-ARG URL=
-ARG BRANCH=
-ARG SHA=
+ARG GO_VERSION="1.17.13"
+
+ARG CHANNEL
+ARG URL
+ARG BRANCH
+ARG SHA
+ARG TARGETARCH
+
+ADD https://go.dev/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz /go.tar.gz
# Basic dependencies.
-ENV HOME /node
-ENV DEBIAN_FRONTEND noninteractive
+ENV HOME="/node" DEBIAN_FRONTEND="noninteractive" GOPATH="/dist"
+
RUN apt-get update && \
- apt-get install -y \
+ apt-get install -y --no-install-recommends \
+ ca-certificates \
apt-utils \
bsdmainutils \
curl \
git \
- git-core \
- python3
+ && rm -rf /var/lib/apt/lists/* && \
+ \
+ tar -C /usr/local -xzf /go.tar.gz && \
+ rm -rf /go.tar.gz
-COPY ./docker/files/ /node/files
-COPY ./installer/genesis /node/files/run/genesis
-COPY ./cmd/updater/update.sh /node/files/build/update.sh
-COPY ./installer/config.json.example /node/files/build/config.json
+ENV PATH="/usr/local/go/bin:${PATH}"
-RUN find /node/files
+COPY ./docker/files/ /dist/files
+COPY ./installer/genesis /dist/files/run/genesis
+COPY ./cmd/updater/update.sh /dist/files/build/update.sh
+COPY ./installer/config.json.example /dist/files/run/config.json.example
# Install algod binaries.
-RUN /node/files/build/install.sh \
- -p "/node/bin" \
- -d "/node/data" \
+RUN /dist/files/build/install.sh \
+ -p "${GOPATH}/bin" \
+ -d "/algod/data" \
-c "${CHANNEL}" \
-u "${URL}" \
-b "${BRANCH}" \
-s "${SHA}"
-# Copy binaries into a clean image
-# TODO: We don't need most of the binaries.
-# Should we delete everything except goal/algod/algocfg/tealdbg?
FROM debian:bullseye-slim as final
-COPY --from=builder "/node/bin/" "/node/bin"
-COPY --from=builder "/node/data/" "/node/dataTemplate"
-COPY --from=builder "/node/files/run" "/node/run"
-ENV BIN_DIR="/node/bin"
-ENV PATH="$BIN_DIR:${PATH}"
-ENV ALGOD_PORT=8080
-ENV ALGORAND_DATA="/algod/data"
-RUN mkdir -p "$ALGORAND_DATA"
-WORKDIR /node/data
+ENV PATH="/node/bin:${PATH}" ALGOD_PORT="8080" ALGORAND_DATA="/algod/data"
# curl is needed to lookup the fast catchup url
-RUN apt-get update && apt-get install -y \
- curl \
- && rm -rf /var/lib/apt/lists/*
+RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && \
+ rm -rf /var/lib/apt/lists/* && \
+ mkdir -p "$ALGORAND_DATA" && \
+ groupadd --system algorand && \
+ useradd --no-log-init --create-home --system --gid algorand algorand && \
+ chown -R algorand:algorand /algod
-# TODO: This works fine, but causes problems when mounting a volume
-# Use algorand user instead of root
-#RUN groupadd -r algorand && \
-# useradd --no-log-init -r -g algorand algorand && \
-# chown -R algorand.algorand /node && \
-# chown -R algorand.algorand /algod
-#USER algorand
+USER algorand
-# Algod REST API
-EXPOSE $ALGOD_PORT
+COPY --chown=algorand:algorand --from=builder "/dist/bin/" "/node/bin/"
+COPY --chown=algorand:algorand --from=builder "/dist/files/run/" "/node/run/"
-# Algod Gossip Port
-EXPOSE 4160
+# Expose Algod REST API, Algod Gossip, and Prometheus Metrics ports
+EXPOSE $ALGOD_PORT 4160 9100
-# Prometheus Metrics
-EXPOSE 9100
+WORKDIR /algod
CMD ["/node/run/run.sh"]
-#CMD ["/bin/bash"]
diff --git a/Makefile b/Makefile
index 52c955b38..a418c7e35 100644
--- a/Makefile
+++ b/Makefile
@@ -85,7 +85,7 @@ GOLDFLAGS := $(GOLDFLAGS_BASE) \
UNIT_TEST_SOURCES := $(sort $(shell GOPATH=$(GOPATH) && GO111MODULE=off && go list ./... | grep -v /go-algorand/test/ ))
ALGOD_API_PACKAGES := $(sort $(shell GOPATH=$(GOPATH) && GO111MODULE=off && cd daemon/algod/api; go list ./... ))
-MSGP_GENERATE := ./protocol ./protocol/test ./crypto ./crypto/merklearray ./crypto/merklesignature ./crypto/stateproof ./data/basics ./data/transactions ./data/stateproofmsg ./data/committee ./data/bookkeeping ./data/hashable ./agreement ./rpcs ./node ./ledger ./ledger/ledgercore ./ledger/store ./stateproof ./data/account ./daemon/algod/api/spec/v2
+MSGP_GENERATE := ./protocol ./protocol/test ./crypto ./crypto/merklearray ./crypto/merklesignature ./crypto/stateproof ./data/basics ./data/transactions ./data/stateproofmsg ./data/committee ./data/bookkeeping ./data/hashable ./agreement ./rpcs ./node ./ledger ./ledger/ledgercore ./ledger/store ./ledger/encoded ./stateproof ./data/account ./daemon/algod/api/spec/v2
default: build
diff --git a/README.md b/README.md
index a935722f7..9e787d6d4 100644
--- a/README.md
+++ b/README.md
@@ -192,6 +192,6 @@ A number of packages provide utilities for the various components:
Please see the [COPYING_FAQ](COPYING_FAQ) for details about how to apply our license.
-Copyright (C) 2019-2022, Algorand Inc.
+Copyright (C) 2019-2023, Algorand Inc.
[developer site url]: https://developer.algorand.org/
diff --git a/agreement/abstractions.go b/agreement/abstractions.go
index cf58aa372..f5d09dc1e 100644
--- a/agreement/abstractions.go
+++ b/agreement/abstractions.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/actions.go b/agreement/actions.go
index ca33c18db..833dc8af3 100644
--- a/agreement/actions.go
+++ b/agreement/actions.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/actor.go b/agreement/actor.go
index 96106eb13..e02c5eb4c 100644
--- a/agreement/actor.go
+++ b/agreement/actor.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/agreeInstall.go b/agreement/agreeInstall.go
index 1028eedb9..28b1985b2 100644
--- a/agreement/agreeInstall.go
+++ b/agreement/agreeInstall.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/agreementtest/keyManager.go b/agreement/agreementtest/keyManager.go
index fe86fad82..6222cffb6 100644
--- a/agreement/agreementtest/keyManager.go
+++ b/agreement/agreementtest/keyManager.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/agreementtest/simulate.go b/agreement/agreementtest/simulate.go
index cafc4e5f3..225d0632c 100644
--- a/agreement/agreementtest/simulate.go
+++ b/agreement/agreementtest/simulate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -170,7 +170,10 @@ func Simulate(dbname string, n basics.Round, roundDeadline time.Duration, ledger
}
_ = accessor
- service := agreement.MakeService(parameters)
+ service, err := agreement.MakeService(parameters)
+ if err != nil {
+ return err
+ }
service.Start()
defer service.Shutdown()
defer stopwatch.shutdown()
diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go
index 173f91fad..6d8b2b43b 100644
--- a/agreement/agreementtest/simulate_test.go
+++ b/agreement/agreementtest/simulate_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -33,6 +33,7 @@ import (
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/account"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/committee"
"github.com/algorand/go-algorand/logging"
@@ -310,7 +311,7 @@ func TestSimulate(t *testing.T) {
// generate accounts
genesis := make(map[basics.Address]basics.AccountData)
incentivePoolAtStart := uint64(1000 * 1000)
- accData := basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: incentivePoolAtStart})
+ accData := basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: incentivePoolAtStart})
genesis[poolAddr] = accData
gen := rand.New(rand.NewSource(2))
diff --git a/agreement/asyncVoteVerifier.go b/agreement/asyncVoteVerifier.go
index 877c92dfb..e30c9c5b1 100644
--- a/agreement/asyncVoteVerifier.go
+++ b/agreement/asyncVoteVerifier.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/asyncVoteVerifier_test.go b/agreement/asyncVoteVerifier_test.go
index 8a4e81959..e344d10b7 100644
--- a/agreement/asyncVoteVerifier_test.go
+++ b/agreement/asyncVoteVerifier_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -34,6 +34,9 @@ func (fp *expiredExecPool) EnqueueBacklog(enqueueCtx context.Context, t execpool
// generate an error, to see if we correctly report that on the verifyVote() call.
return context.Canceled
}
+func (fp *expiredExecPool) BufferSize() (length, capacity int) {
+ return
+}
// Test async vote verifier against a full execution pool.
func TestVerificationAgainstFullExecutionPool(t *testing.T) {
diff --git a/agreement/autopsy.go b/agreement/autopsy.go
index 2b344f0a6..e940ae4bc 100644
--- a/agreement/autopsy.go
+++ b/agreement/autopsy.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/bundle.go b/agreement/bundle.go
index de297110e..a7670b53d 100644
--- a/agreement/bundle.go
+++ b/agreement/bundle.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/bundle_test.go b/agreement/bundle_test.go
index 615b2c508..4542b0cd7 100644
--- a/agreement/bundle_test.go
+++ b/agreement/bundle_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/cadaver.go b/agreement/cadaver.go
index d3f626ada..3b5c8a78c 100644
--- a/agreement/cadaver.go
+++ b/agreement/cadaver.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -48,6 +48,7 @@ type cadaver struct {
overrideSetup bool // if true, do not execute code in trySetup
baseFilename string // no logging happens if this is ""
+ baseDirectory string // if empty, will be data directory
fileSizeTarget int64
out *cadaverHandle
@@ -60,11 +61,14 @@ type cadaver struct {
}
func (c *cadaver) filename() string {
- // Put cadaver files in our data directory
- p := config.GetCurrentVersion().DataDirectory
+ baseDir := c.baseDirectory
+ if baseDir == "" {
+ // Put cadaver files in our data directory
+ baseDir = config.GetCurrentVersion().DataDirectory
+ }
fmtstr := "%s.cdv"
- return filepath.Join(p, fmt.Sprintf(fmtstr, c.baseFilename))
+ return filepath.Join(baseDir, fmt.Sprintf(fmtstr, c.baseFilename))
}
func (c *cadaver) init() (err error) {
@@ -115,6 +119,7 @@ func (c *cadaver) trySetup() bool {
if c.out == nil {
err := c.init()
if err != nil {
+ logging.Base().Warn(err)
c.failed = err
return false
}
diff --git a/agreement/certificate.go b/agreement/certificate.go
index 34c4192d8..6ce4b9e0a 100644
--- a/agreement/certificate.go
+++ b/agreement/certificate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/certificate_test.go b/agreement/certificate_test.go
index 4881ac244..9ae2ebfa2 100644
--- a/agreement/certificate_test.go
+++ b/agreement/certificate_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/common_test.go b/agreement/common_test.go
index fa43d1091..361ab2e2a 100644
--- a/agreement/common_test.go
+++ b/agreement/common_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/coservice.go b/agreement/coservice.go
index 0126cb85d..7fe4ab1f5 100644
--- a/agreement/coservice.go
+++ b/agreement/coservice.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/cryptoRequestContext.go b/agreement/cryptoRequestContext.go
index 88530e357..8807e3f57 100644
--- a/agreement/cryptoRequestContext.go
+++ b/agreement/cryptoRequestContext.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/cryptoRequestContext_test.go b/agreement/cryptoRequestContext_test.go
index d41533f2a..44b279aee 100644
--- a/agreement/cryptoRequestContext_test.go
+++ b/agreement/cryptoRequestContext_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/cryptoVerifier.go b/agreement/cryptoVerifier.go
index ca4bceb66..70609a634 100644
--- a/agreement/cryptoVerifier.go
+++ b/agreement/cryptoVerifier.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/cryptoVerifier_test.go b/agreement/cryptoVerifier_test.go
index 21b78c601..880702b8d 100644
--- a/agreement/cryptoVerifier_test.go
+++ b/agreement/cryptoVerifier_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/demux.go b/agreement/demux.go
index ad51038b4..8d96444d5 100644
--- a/agreement/demux.go
+++ b/agreement/demux.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/demux_test.go b/agreement/demux_test.go
index 7d7bbc71b..692d2cc5f 100644
--- a/agreement/demux_test.go
+++ b/agreement/demux_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/doc.go b/agreement/doc.go
index ffbc79fa0..0d4ae97af 100644
--- a/agreement/doc.go
+++ b/agreement/doc.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/encoding_test.go b/agreement/encoding_test.go
index 9e899ca5c..7554ec446 100644
--- a/agreement/encoding_test.go
+++ b/agreement/encoding_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/errors.go b/agreement/errors.go
index eae272b9f..e0929b63e 100644
--- a/agreement/errors.go
+++ b/agreement/errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/events.go b/agreement/events.go
index f418cc38f..5304b1798 100644
--- a/agreement/events.go
+++ b/agreement/events.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -947,6 +947,6 @@ func (e messageEvent) AttachValidatedAt(d time.Duration) messageEvent {
}
func (e messageEvent) AttachReceivedAt(d time.Duration) messageEvent {
- e.Input.Proposal.receivedAt = d
+ e.Input.UnauthenticatedProposal.receivedAt = d
return e
}
diff --git a/agreement/events_test.go b/agreement/events_test.go
index 243dd0508..c558f0067 100644
--- a/agreement/events_test.go
+++ b/agreement/events_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -21,6 +21,7 @@ import (
"testing"
"github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/test/partitiontest"
"github.com/stretchr/testify/require"
)
@@ -28,6 +29,7 @@ import (
// properly decoded from ConsensusVersionView.
// This test is only needed for agreement state serialization switch from reflection to msgp.
func TestSerializableErrorBackwardCompatibility(t *testing.T) {
+ partitiontest.PartitionTest(t)
encodedEmpty, err := base64.StdEncoding.DecodeString("gqNFcnLAp1ZlcnNpb26jdjEw")
require.NoError(t, err)
diff --git a/agreement/fuzzer/bandwidthFilter_test.go b/agreement/fuzzer/bandwidthFilter_test.go
index e5a31bf2e..93b958f18 100644
--- a/agreement/fuzzer/bandwidthFilter_test.go
+++ b/agreement/fuzzer/bandwidthFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/catchupFilter_test.go b/agreement/fuzzer/catchupFilter_test.go
index 64493d840..2ef23d44b 100644
--- a/agreement/fuzzer/catchupFilter_test.go
+++ b/agreement/fuzzer/catchupFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/clockedFilter_test.go b/agreement/fuzzer/clockedFilter_test.go
index ceab6560a..ee4884acd 100644
--- a/agreement/fuzzer/clockedFilter_test.go
+++ b/agreement/fuzzer/clockedFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/dropMessageFilter_test.go b/agreement/fuzzer/dropMessageFilter_test.go
index 519782c77..976a1d448 100644
--- a/agreement/fuzzer/dropMessageFilter_test.go
+++ b/agreement/fuzzer/dropMessageFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/duplicateMessageFilter_test.go b/agreement/fuzzer/duplicateMessageFilter_test.go
index 6d0e25f90..80a4f9b45 100644
--- a/agreement/fuzzer/duplicateMessageFilter_test.go
+++ b/agreement/fuzzer/duplicateMessageFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/filter_test.go b/agreement/fuzzer/filter_test.go
index ede2d907e..b4b0b7972 100644
--- a/agreement/fuzzer/filter_test.go
+++ b/agreement/fuzzer/filter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/fuzzer.go b/agreement/fuzzer/fuzzer.go
index 3c404015b..847810f45 100644
--- a/agreement/fuzzer/fuzzer.go
+++ b/agreement/fuzzer/fuzzer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/fuzzer_test.go b/agreement/fuzzer/fuzzer_test.go
index e5437f8bc..332b2fb87 100644
--- a/agreement/fuzzer/fuzzer_test.go
+++ b/agreement/fuzzer/fuzzer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -150,7 +150,10 @@ func (n *Fuzzer) initAgreementNode(nodeID int, filters ...NetworkFilterFactory)
cadaverFilename = ""
}
- n.agreements[nodeID] = agreement.MakeService(n.agreementParams[nodeID])
+ n.agreements[nodeID], err = agreement.MakeService(n.agreementParams[nodeID])
+ if err != nil {
+ return false
+ }
n.agreements[nodeID].SetTracerFilename(cadaverFilename)
@@ -578,7 +581,11 @@ func (n *Fuzzer) CrashNode(nodeID int) {
n.ledgers[nodeID].ClearNotifications()
n.agreementParams[nodeID].Network = gossip.WrapNetwork(n.facades[nodeID], n.log, config.GetDefaultLocal())
- n.agreements[nodeID] = agreement.MakeService(n.agreementParams[nodeID])
+ var err error
+ n.agreements[nodeID], err = agreement.MakeService(n.agreementParams[nodeID])
+ if err != nil {
+ panic(err)
+ }
cadaverFilename := fmt.Sprintf("%v-%v", n.networkName, nodeID)
if n.disableTraces == true {
diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go
index 2ae73dd69..c0ffb8b53 100644
--- a/agreement/fuzzer/ledger_test.go
+++ b/agreement/fuzzer/ledger_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/messageDecoderFilter_test.go b/agreement/fuzzer/messageDecoderFilter_test.go
index cb80ed990..03b0af15d 100644
--- a/agreement/fuzzer/messageDecoderFilter_test.go
+++ b/agreement/fuzzer/messageDecoderFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/messageDelayFilter_test.go b/agreement/fuzzer/messageDelayFilter_test.go
index 04cf55081..02636b50a 100644
--- a/agreement/fuzzer/messageDelayFilter_test.go
+++ b/agreement/fuzzer/messageDelayFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/messageDuplicationFilter_test.go b/agreement/fuzzer/messageDuplicationFilter_test.go
index 747301a11..4a7d4e88c 100644
--- a/agreement/fuzzer/messageDuplicationFilter_test.go
+++ b/agreement/fuzzer/messageDuplicationFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/messagePriorityQueue_test.go b/agreement/fuzzer/messagePriorityQueue_test.go
index bbb9f1364..8638cb48b 100644
--- a/agreement/fuzzer/messagePriorityQueue_test.go
+++ b/agreement/fuzzer/messagePriorityQueue_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/messageReflectionFilter_test.go b/agreement/fuzzer/messageReflectionFilter_test.go
index c1b58ef27..73aeae5b1 100644
--- a/agreement/fuzzer/messageReflectionFilter_test.go
+++ b/agreement/fuzzer/messageReflectionFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/messageRegossipFilter_test.go b/agreement/fuzzer/messageRegossipFilter_test.go
index 628db283a..264de6e64 100644
--- a/agreement/fuzzer/messageRegossipFilter_test.go
+++ b/agreement/fuzzer/messageRegossipFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/messageReorderingFilter_test.go b/agreement/fuzzer/messageReorderingFilter_test.go
index 79680ab88..23049deae 100644
--- a/agreement/fuzzer/messageReorderingFilter_test.go
+++ b/agreement/fuzzer/messageReorderingFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/networkFacade_test.go b/agreement/fuzzer/networkFacade_test.go
index 5c7018ef0..d00ec4e7c 100644
--- a/agreement/fuzzer/networkFacade_test.go
+++ b/agreement/fuzzer/networkFacade_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/nodeCrashFilter_test.go b/agreement/fuzzer/nodeCrashFilter_test.go
index a15930b94..ef31ca4d8 100644
--- a/agreement/fuzzer/nodeCrashFilter_test.go
+++ b/agreement/fuzzer/nodeCrashFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/nullFilter_test.go b/agreement/fuzzer/nullFilter_test.go
index 0cc7300c3..e6ee528e1 100644
--- a/agreement/fuzzer/nullFilter_test.go
+++ b/agreement/fuzzer/nullFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/router_test.go b/agreement/fuzzer/router_test.go
index f63bb389e..74e39c1ec 100644
--- a/agreement/fuzzer/router_test.go
+++ b/agreement/fuzzer/router_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/schedulerFilter_test.go b/agreement/fuzzer/schedulerFilter_test.go
index dd48aa533..d6e00e773 100644
--- a/agreement/fuzzer/schedulerFilter_test.go
+++ b/agreement/fuzzer/schedulerFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/tests_test.go b/agreement/fuzzer/tests_test.go
index 2dfc83707..fa48979a3 100644
--- a/agreement/fuzzer/tests_test.go
+++ b/agreement/fuzzer/tests_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/topologyFilter_test.go b/agreement/fuzzer/topologyFilter_test.go
index 841e9eb04..db62ff604 100644
--- a/agreement/fuzzer/topologyFilter_test.go
+++ b/agreement/fuzzer/topologyFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/trafficStatisticsFilter_test.go b/agreement/fuzzer/trafficStatisticsFilter_test.go
index 802583c4d..72cad0fc6 100644
--- a/agreement/fuzzer/trafficStatisticsFilter_test.go
+++ b/agreement/fuzzer/trafficStatisticsFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/validator_test.go b/agreement/fuzzer/validator_test.go
index 95a198e5c..87425a099 100644
--- a/agreement/fuzzer/validator_test.go
+++ b/agreement/fuzzer/validator_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/fuzzer/voteFilter_test.go b/agreement/fuzzer/voteFilter_test.go
index e99a19b31..655708758 100644
--- a/agreement/fuzzer/voteFilter_test.go
+++ b/agreement/fuzzer/voteFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/gossip/network.go b/agreement/gossip/network.go
index 43eefd2db..334188277 100644
--- a/agreement/gossip/network.go
+++ b/agreement/gossip/network.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/gossip/networkFull_test.go b/agreement/gossip/networkFull_test.go
index ae5088d2d..784baa8c0 100644
--- a/agreement/gossip/networkFull_test.go
+++ b/agreement/gossip/networkFull_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/gossip/network_test.go b/agreement/gossip/network_test.go
index 063fe8434..bd4f4baf3 100644
--- a/agreement/gossip/network_test.go
+++ b/agreement/gossip/network_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/keyManager_test.go b/agreement/keyManager_test.go
index 43ab18769..106431c91 100644
--- a/agreement/keyManager_test.go
+++ b/agreement/keyManager_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/listener.go b/agreement/listener.go
index b0dafaff7..22ec28585 100644
--- a/agreement/listener.go
+++ b/agreement/listener.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/message.go b/agreement/message.go
index a1f6a8c80..d7d52bc8a 100644
--- a/agreement/message.go
+++ b/agreement/message.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/message_test.go b/agreement/message_test.go
index 76209a5f9..3c3ab77f0 100644
--- a/agreement/message_test.go
+++ b/agreement/message_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/msgp_gen.go b/agreement/msgp_gen.go
index 3c396226b..2fa1e11a0 100644
--- a/agreement/msgp_gen.go
+++ b/agreement/msgp_gen.go
@@ -3791,7 +3791,7 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) {
o = msgp.Require(b, z.Msgsize())
// omitempty: check for empty values
zb0004Len := uint32(29)
- var zb0004Mask uint64 /* 38 bits */
+ var zb0004Mask uint64 /* 39 bits */
if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 {
zb0004Len--
zb0004Mask |= 0x40
@@ -3854,59 +3854,59 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) {
}
if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x400000
+ zb0004Mask |= 0x800000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x800000
+ zb0004Mask |= 0x1000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x1000000
+ zb0004Mask |= 0x2000000
}
if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x2000000
+ zb0004Mask |= 0x4000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x4000000
+ zb0004Mask |= 0x8000000
}
if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 {
zb0004Len--
- zb0004Mask |= 0x8000000
+ zb0004Mask |= 0x10000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 {
zb0004Len--
- zb0004Mask |= 0x10000000
+ zb0004Mask |= 0x20000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 {
zb0004Len--
- zb0004Mask |= 0x20000000
+ zb0004Mask |= 0x40000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x40000000
+ zb0004Mask |= 0x80000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x80000000
+ zb0004Mask |= 0x100000000
}
if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x100000000
+ zb0004Mask |= 0x200000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x200000000
+ zb0004Mask |= 0x400000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x400000000
+ zb0004Mask |= 0x800000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false {
zb0004Len--
- zb0004Mask |= 0x800000000
+ zb0004Mask |= 0x1000000000
}
// variable map header, size zb0004Len
o = msgp.AppendMapHeader(o, zb0004Len)
@@ -3993,32 +3993,32 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) {
o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65)
o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate)
}
- if (zb0004Mask & 0x400000) == 0 { // if not empty
+ if (zb0004Mask & 0x800000) == 0 { // if not empty
// string "rnd"
o = append(o, 0xa3, 0x72, 0x6e, 0x64)
o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o)
}
- if (zb0004Mask & 0x800000) == 0 { // if not empty
+ if (zb0004Mask & 0x1000000) == 0 { // if not empty
// string "rwcalr"
o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72)
o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o)
}
- if (zb0004Mask & 0x1000000) == 0 { // if not empty
+ if (zb0004Mask & 0x2000000) == 0 { // if not empty
// string "rwd"
o = append(o, 0xa3, 0x72, 0x77, 0x64)
o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o)
}
- if (zb0004Mask & 0x2000000) == 0 { // if not empty
+ if (zb0004Mask & 0x4000000) == 0 { // if not empty
// string "sdpf"
o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66)
o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o)
}
- if (zb0004Mask & 0x4000000) == 0 { // if not empty
+ if (zb0004Mask & 0x8000000) == 0 { // if not empty
// string "seed"
o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64)
o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o)
}
- if (zb0004Mask & 0x8000000) == 0 { // if not empty
+ if (zb0004Mask & 0x10000000) == 0 { // if not empty
// string "spt"
o = append(o, 0xa3, 0x73, 0x70, 0x74)
if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil {
@@ -4038,42 +4038,42 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) {
o = zb0002.MarshalMsg(o)
}
}
- if (zb0004Mask & 0x10000000) == 0 { // if not empty
+ if (zb0004Mask & 0x20000000) == 0 { // if not empty
// string "tc"
o = append(o, 0xa2, 0x74, 0x63)
o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter)
}
- if (zb0004Mask & 0x20000000) == 0 { // if not empty
+ if (zb0004Mask & 0x40000000) == 0 { // if not empty
// string "ts"
o = append(o, 0xa2, 0x74, 0x73)
o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp)
}
- if (zb0004Mask & 0x40000000) == 0 { // if not empty
+ if (zb0004Mask & 0x80000000) == 0 { // if not empty
// string "txn"
o = append(o, 0xa3, 0x74, 0x78, 0x6e)
o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o)
}
- if (zb0004Mask & 0x80000000) == 0 { // if not empty
+ if (zb0004Mask & 0x100000000) == 0 { // if not empty
// string "txn256"
o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36)
o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o)
}
- if (zb0004Mask & 0x100000000) == 0 { // if not empty
+ if (zb0004Mask & 0x200000000) == 0 { // if not empty
// string "txns"
o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73)
o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o)
}
- if (zb0004Mask & 0x200000000) == 0 { // if not empty
+ if (zb0004Mask & 0x400000000) == 0 { // if not empty
// string "upgradedelay"
o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79)
o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o)
}
- if (zb0004Mask & 0x400000000) == 0 { // if not empty
+ if (zb0004Mask & 0x800000000) == 0 { // if not empty
// string "upgradeprop"
o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70)
o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o)
}
- if (zb0004Mask & 0x800000000) == 0 { // if not empty
+ if (zb0004Mask & 0x1000000000) == 0 { // if not empty
// string "upgradeyes"
o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73)
o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove)
@@ -7839,7 +7839,7 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) {
o = msgp.Require(b, z.Msgsize())
// omitempty: check for empty values
zb0004Len := uint32(30)
- var zb0004Mask uint64 /* 37 bits */
+ var zb0004Mask uint64 /* 38 bits */
if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 {
zb0004Len--
zb0004Mask |= 0x80
@@ -7906,59 +7906,59 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) {
}
if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x800000
+ zb0004Mask |= 0x1000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x1000000
+ zb0004Mask |= 0x2000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x2000000
+ zb0004Mask |= 0x4000000
}
if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x4000000
+ zb0004Mask |= 0x8000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x8000000
+ zb0004Mask |= 0x10000000
}
if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 {
zb0004Len--
- zb0004Mask |= 0x10000000
+ zb0004Mask |= 0x20000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 {
zb0004Len--
- zb0004Mask |= 0x20000000
+ zb0004Mask |= 0x40000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 {
zb0004Len--
- zb0004Mask |= 0x40000000
+ zb0004Mask |= 0x80000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x80000000
+ zb0004Mask |= 0x100000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x100000000
+ zb0004Mask |= 0x200000000
}
if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x200000000
+ zb0004Mask |= 0x400000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x400000000
+ zb0004Mask |= 0x800000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x800000000
+ zb0004Mask |= 0x1000000000
}
if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false {
zb0004Len--
- zb0004Mask |= 0x1000000000
+ zb0004Mask |= 0x2000000000
}
// variable map header, size zb0004Len
o = msgp.AppendMapHeader(o, zb0004Len)
@@ -8050,32 +8050,32 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) {
o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65)
o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate)
}
- if (zb0004Mask & 0x800000) == 0 { // if not empty
+ if (zb0004Mask & 0x1000000) == 0 { // if not empty
// string "rnd"
o = append(o, 0xa3, 0x72, 0x6e, 0x64)
o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o)
}
- if (zb0004Mask & 0x1000000) == 0 { // if not empty
+ if (zb0004Mask & 0x2000000) == 0 { // if not empty
// string "rwcalr"
o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72)
o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o)
}
- if (zb0004Mask & 0x2000000) == 0 { // if not empty
+ if (zb0004Mask & 0x4000000) == 0 { // if not empty
// string "rwd"
o = append(o, 0xa3, 0x72, 0x77, 0x64)
o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o)
}
- if (zb0004Mask & 0x4000000) == 0 { // if not empty
+ if (zb0004Mask & 0x8000000) == 0 { // if not empty
// string "sdpf"
o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66)
o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o)
}
- if (zb0004Mask & 0x8000000) == 0 { // if not empty
+ if (zb0004Mask & 0x10000000) == 0 { // if not empty
// string "seed"
o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64)
o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o)
}
- if (zb0004Mask & 0x10000000) == 0 { // if not empty
+ if (zb0004Mask & 0x20000000) == 0 { // if not empty
// string "spt"
o = append(o, 0xa3, 0x73, 0x70, 0x74)
if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil {
@@ -8095,42 +8095,42 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) {
o = zb0002.MarshalMsg(o)
}
}
- if (zb0004Mask & 0x20000000) == 0 { // if not empty
+ if (zb0004Mask & 0x40000000) == 0 { // if not empty
// string "tc"
o = append(o, 0xa2, 0x74, 0x63)
o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter)
}
- if (zb0004Mask & 0x40000000) == 0 { // if not empty
+ if (zb0004Mask & 0x80000000) == 0 { // if not empty
// string "ts"
o = append(o, 0xa2, 0x74, 0x73)
o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp)
}
- if (zb0004Mask & 0x80000000) == 0 { // if not empty
+ if (zb0004Mask & 0x100000000) == 0 { // if not empty
// string "txn"
o = append(o, 0xa3, 0x74, 0x78, 0x6e)
o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o)
}
- if (zb0004Mask & 0x100000000) == 0 { // if not empty
+ if (zb0004Mask & 0x200000000) == 0 { // if not empty
// string "txn256"
o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36)
o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o)
}
- if (zb0004Mask & 0x200000000) == 0 { // if not empty
+ if (zb0004Mask & 0x400000000) == 0 { // if not empty
// string "txns"
o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73)
o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o)
}
- if (zb0004Mask & 0x400000000) == 0 { // if not empty
+ if (zb0004Mask & 0x800000000) == 0 { // if not empty
// string "upgradedelay"
o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79)
o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o)
}
- if (zb0004Mask & 0x800000000) == 0 { // if not empty
+ if (zb0004Mask & 0x1000000000) == 0 { // if not empty
// string "upgradeprop"
o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70)
o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o)
}
- if (zb0004Mask & 0x1000000000) == 0 { // if not empty
+ if (zb0004Mask & 0x2000000000) == 0 { // if not empty
// string "upgradeyes"
o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73)
o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove)
@@ -9415,7 +9415,7 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) {
o = msgp.Require(b, z.Msgsize())
// omitempty: check for empty values
zb0004Len := uint32(29)
- var zb0004Mask uint64 /* 35 bits */
+ var zb0004Mask uint64 /* 36 bits */
if (*z).Block.BlockHeader.RewardsState.RewardsLevel == 0 {
zb0004Len--
zb0004Mask |= 0x40
@@ -9478,59 +9478,59 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) {
}
if (*z).Block.BlockHeader.Round.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x200000
+ zb0004Mask |= 0x400000
}
if (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x400000
+ zb0004Mask |= 0x800000
}
if (*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x800000
+ zb0004Mask |= 0x1000000
}
if (*z).SeedProof.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x1000000
+ zb0004Mask |= 0x2000000
}
if (*z).Block.BlockHeader.Seed.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x2000000
+ zb0004Mask |= 0x4000000
}
if len((*z).Block.BlockHeader.StateProofTracking) == 0 {
zb0004Len--
- zb0004Mask |= 0x4000000
+ zb0004Mask |= 0x8000000
}
if (*z).Block.BlockHeader.TxnCounter == 0 {
zb0004Len--
- zb0004Mask |= 0x8000000
+ zb0004Mask |= 0x10000000
}
if (*z).Block.BlockHeader.TimeStamp == 0 {
zb0004Len--
- zb0004Mask |= 0x10000000
+ zb0004Mask |= 0x20000000
}
if (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x20000000
+ zb0004Mask |= 0x40000000
}
if (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x40000000
+ zb0004Mask |= 0x80000000
}
if (*z).Block.Payset.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x80000000
+ zb0004Mask |= 0x100000000
}
if (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x100000000
+ zb0004Mask |= 0x200000000
}
if (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() {
zb0004Len--
- zb0004Mask |= 0x200000000
+ zb0004Mask |= 0x400000000
}
if (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false {
zb0004Len--
- zb0004Mask |= 0x400000000
+ zb0004Mask |= 0x800000000
}
// variable map header, size zb0004Len
o = msgp.AppendMapHeader(o, zb0004Len)
@@ -9617,32 +9617,32 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) {
o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65)
o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsRate)
}
- if (zb0004Mask & 0x200000) == 0 { // if not empty
+ if (zb0004Mask & 0x400000) == 0 { // if not empty
// string "rnd"
o = append(o, 0xa3, 0x72, 0x6e, 0x64)
o = (*z).Block.BlockHeader.Round.MarshalMsg(o)
}
- if (zb0004Mask & 0x400000) == 0 { // if not empty
+ if (zb0004Mask & 0x800000) == 0 { // if not empty
// string "rwcalr"
o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72)
o = (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o)
}
- if (zb0004Mask & 0x800000) == 0 { // if not empty
+ if (zb0004Mask & 0x1000000) == 0 { // if not empty
// string "rwd"
o = append(o, 0xa3, 0x72, 0x77, 0x64)
o = (*z).Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o)
}
- if (zb0004Mask & 0x1000000) == 0 { // if not empty
+ if (zb0004Mask & 0x2000000) == 0 { // if not empty
// string "sdpf"
o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66)
o = (*z).SeedProof.MarshalMsg(o)
}
- if (zb0004Mask & 0x2000000) == 0 { // if not empty
+ if (zb0004Mask & 0x4000000) == 0 { // if not empty
// string "seed"
o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64)
o = (*z).Block.BlockHeader.Seed.MarshalMsg(o)
}
- if (zb0004Mask & 0x4000000) == 0 { // if not empty
+ if (zb0004Mask & 0x8000000) == 0 { // if not empty
// string "spt"
o = append(o, 0xa3, 0x73, 0x70, 0x74)
if (*z).Block.BlockHeader.StateProofTracking == nil {
@@ -9662,42 +9662,42 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) {
o = zb0002.MarshalMsg(o)
}
}
- if (zb0004Mask & 0x8000000) == 0 { // if not empty
+ if (zb0004Mask & 0x10000000) == 0 { // if not empty
// string "tc"
o = append(o, 0xa2, 0x74, 0x63)
o = msgp.AppendUint64(o, (*z).Block.BlockHeader.TxnCounter)
}
- if (zb0004Mask & 0x10000000) == 0 { // if not empty
+ if (zb0004Mask & 0x20000000) == 0 { // if not empty
// string "ts"
o = append(o, 0xa2, 0x74, 0x73)
o = msgp.AppendInt64(o, (*z).Block.BlockHeader.TimeStamp)
}
- if (zb0004Mask & 0x20000000) == 0 { // if not empty
+ if (zb0004Mask & 0x40000000) == 0 { // if not empty
// string "txn"
o = append(o, 0xa3, 0x74, 0x78, 0x6e)
o = (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o)
}
- if (zb0004Mask & 0x40000000) == 0 { // if not empty
+ if (zb0004Mask & 0x80000000) == 0 { // if not empty
// string "txn256"
o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36)
o = (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o)
}
- if (zb0004Mask & 0x80000000) == 0 { // if not empty
+ if (zb0004Mask & 0x100000000) == 0 { // if not empty
// string "txns"
o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73)
o = (*z).Block.Payset.MarshalMsg(o)
}
- if (zb0004Mask & 0x100000000) == 0 { // if not empty
+ if (zb0004Mask & 0x200000000) == 0 { // if not empty
// string "upgradedelay"
o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79)
o = (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o)
}
- if (zb0004Mask & 0x200000000) == 0 { // if not empty
+ if (zb0004Mask & 0x400000000) == 0 { // if not empty
// string "upgradeprop"
o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70)
o = (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o)
}
- if (zb0004Mask & 0x400000000) == 0 { // if not empty
+ if (zb0004Mask & 0x800000000) == 0 { // if not empty
// string "upgradeyes"
o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73)
o = msgp.AppendBool(o, (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove)
diff --git a/agreement/params.go b/agreement/params.go
index 168c25260..de90d2913 100644
--- a/agreement/params.go
+++ b/agreement/params.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/persistence.go b/agreement/persistence.go
index 497e4b9af..5e85955b3 100644
--- a/agreement/persistence.go
+++ b/agreement/persistence.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/persistence_test.go b/agreement/persistence_test.go
index 7a4ec3db4..fbd9323b0 100644
--- a/agreement/persistence_test.go
+++ b/agreement/persistence_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -240,6 +240,7 @@ func TestEmptyMapDeserialization(t *testing.T) {
}
func TestDecodeFailures(t *testing.T) {
+ partitiontest.PartitionTest(t)
clock := timers.MakeMonotonicClock(time.Date(2015, 1, 2, 5, 6, 7, 8, time.UTC))
ce := clock.Encode()
log := makeServiceLogger(logging.Base())
diff --git a/agreement/player.go b/agreement/player.go
index cc29240aa..1ae552b0b 100644
--- a/agreement/player.go
+++ b/agreement/player.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/playerContract.go b/agreement/playerContract.go
index fa95c2278..cfc574fef 100644
--- a/agreement/playerContract.go
+++ b/agreement/playerContract.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/player_permutation_test.go b/agreement/player_permutation_test.go
index d7dcf9add..033509393 100644
--- a/agreement/player_permutation_test.go
+++ b/agreement/player_permutation_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/player_test.go b/agreement/player_test.go
index 3e3cf8167..83368c3d0 100644
--- a/agreement/player_test.go
+++ b/agreement/player_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -19,6 +19,7 @@ package agreement
import (
"fmt"
"testing"
+ "time"
"github.com/stretchr/testify/require"
@@ -3232,4 +3233,89 @@ func TestPlayerAlwaysResynchsPinnedValue(t *testing.T) {
require.Truef(t, trace.Contains(rePayloadEvent), "Player should relay payload even if not staged in previous period")
}
+// test that ReceivedAt and ValidateAt timing information are retained in proposalStore
+// when the payloadPresent and payloadVerified events are processed, and that both timings
+// are available when the ensureAction is called for the block.
+func TestPlayerRetainsReceivedValidatedAt(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ const r = round(20239)
+ const p = period(1001)
+ pWhite, pM, helper := setupP(t, r-1, p, soft)
+ pP, pV := helper.MakeRandomProposalPayload(t, r-1)
+
+ // send a payload
+ // store an arbitrary proposal/payload
+ vVote := helper.MakeVerifiedVote(t, 0, r-1, p, propose, *pV)
+ inMsg := messageEvent{T: voteVerified, Input: message{Vote: vVote, UnauthenticatedVote: vVote.u()}}
+ err, panicErr := pM.transition(inMsg)
+ require.NoError(t, err)
+ require.NoError(t, panicErr)
+
+ // payloadPresent
+ m := message{UnauthenticatedProposal: pP.u()}
+ inMsg = messageEvent{T: payloadPresent, Input: m}
+ inMsg = inMsg.AttachReceivedAt(time.Second)
+ err, panicErr = pM.transition(inMsg)
+ require.NoError(t, err)
+ require.NoError(t, panicErr)
+
+ // make sure payload verify request
+ verifyEvent := ev(cryptoAction{T: verifyPayload, M: m, TaskIndex: 0})
+ require.Truef(t, pM.getTrace().Contains(verifyEvent), "Player should verify payload")
+
+ // payloadVerified
+ inMsg = messageEvent{T: payloadVerified, Input: message{Proposal: *pP}, Proto: ConsensusVersionView{Version: protocol.ConsensusCurrentVersion}}
+ inMsg = inMsg.AttachValidatedAt(2 * time.Second)
+ err, panicErr = pM.transition(inMsg)
+ require.NoError(t, err)
+ require.NoError(t, panicErr)
+
+ // gen cert to move into the next round
+ votes := make([]vote, int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion])))
+ for i := 0; i < int(cert.threshold(config.Consensus[protocol.ConsensusCurrentVersion])); i++ {
+ votes[i] = helper.MakeVerifiedVote(t, i, r-1, p, cert, *pV)
+ }
+ bun := unauthenticatedBundle{
+ Round: r - 1,
+ Period: p,
+ Proposal: *pV,
+ }
+ inMsg = messageEvent{
+ T: bundleVerified,
+ Input: message{
+ Bundle: bundle{
+ U: bun,
+ Votes: votes,
+ },
+ UnauthenticatedBundle: bun,
+ },
+ Proto: ConsensusVersionView{Version: protocol.ConsensusCurrentVersion},
+ }
+ err, panicErr = pM.transition(inMsg)
+ require.NoError(t, err)
+ require.NoError(t, panicErr)
+
+ require.Equalf(t, r, pWhite.Round, "player did not enter new round")
+ require.Equalf(t, period(0), pWhite.Period, "player did not enter period 0 in new round")
+ commitEvent := ev(ensureAction{Certificate: Certificate(bun), Payload: *pP})
+ require.Truef(t, pM.getTrace().Contains(commitEvent), "Player should try to ensure block/digest on ledger")
+
+ // find and unwrap ensureAction from trace
+ var ea ensureAction
+ var foundEA bool
+ for _, ev := range pM.getTrace().events {
+ if wae, ok := ev.(wrappedActionEvent); ok {
+ if wae.action.t() == ensure {
+ require.False(t, foundEA)
+ ea = wae.action.(ensureAction)
+ foundEA = true
+ }
+ }
+ }
+ require.True(t, foundEA)
+ require.Equal(t, 2*time.Second, ea.Payload.validatedAt)
+ require.Equal(t, time.Second, ea.Payload.receivedAt)
+}
+
// todo: test pipelined rounds, and round interruption
diff --git a/agreement/proposal.go b/agreement/proposal.go
index ca0d6e678..ec382fe94 100644
--- a/agreement/proposal.go
+++ b/agreement/proposal.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -59,6 +59,11 @@ type unauthenticatedProposal struct {
OriginalPeriod period `codec:"oper"`
OriginalProposer basics.Address `codec:"oprop"`
+
+ // receivedAt indicates the time at which this proposal was
+ // delivered to the agreement package (as a messageEvent),
+ // relative to the zero of that round.
+ receivedAt time.Duration
}
// TransmittedPayload exported for dumping textual versions of messages
diff --git a/agreement/proposalManager.go b/agreement/proposalManager.go
index 5abe5f052..ca972b56e 100644
--- a/agreement/proposalManager.go
+++ b/agreement/proposalManager.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalManagerContract.go b/agreement/proposalManagerContract.go
index 8f20bd457..df1593848 100644
--- a/agreement/proposalManagerContract.go
+++ b/agreement/proposalManagerContract.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalManager_test.go b/agreement/proposalManager_test.go
index a759739d0..e76de0be5 100644
--- a/agreement/proposalManager_test.go
+++ b/agreement/proposalManager_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalStore.go b/agreement/proposalStore.go
index 841dc91b9..1b50fb6f9 100644
--- a/agreement/proposalStore.go
+++ b/agreement/proposalStore.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -86,6 +86,9 @@ func (a blockAssembler) bind(p proposal) (blockAssembler, error) {
a.Payload = p
a.Assembled = true
+ // remember when the original unauthenticatedProposal was received
+ a.Payload.receivedAt = a.Pipeline.receivedAt
+
return a, nil
}
diff --git a/agreement/proposalStoreContract.go b/agreement/proposalStoreContract.go
index 6d888bc6d..b1d0192ac 100644
--- a/agreement/proposalStoreContract.go
+++ b/agreement/proposalStoreContract.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalStore_test.go b/agreement/proposalStore_test.go
index 237579c84..46f9cd38d 100644
--- a/agreement/proposalStore_test.go
+++ b/agreement/proposalStore_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalTable.go b/agreement/proposalTable.go
index b3ef71ac6..a5a5ff2eb 100644
--- a/agreement/proposalTable.go
+++ b/agreement/proposalTable.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalTable_test.go b/agreement/proposalTable_test.go
index af81305fb..2cb5e9f38 100644
--- a/agreement/proposalTable_test.go
+++ b/agreement/proposalTable_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalTracker.go b/agreement/proposalTracker.go
index e3efcd372..4539a1f51 100644
--- a/agreement/proposalTracker.go
+++ b/agreement/proposalTracker.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalTrackerContract.go b/agreement/proposalTrackerContract.go
index bbe4911c2..2b995dfca 100644
--- a/agreement/proposalTrackerContract.go
+++ b/agreement/proposalTrackerContract.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposalTracker_test.go b/agreement/proposalTracker_test.go
index 71096b4ad..641dee70e 100644
--- a/agreement/proposalTracker_test.go
+++ b/agreement/proposalTracker_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/proposal_test.go b/agreement/proposal_test.go
index 2adfb6c6c..3102a08a8 100644
--- a/agreement/proposal_test.go
+++ b/agreement/proposal_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/pseudonode.go b/agreement/pseudonode.go
index 6075e8ebf..3ebb681ca 100644
--- a/agreement/pseudonode.go
+++ b/agreement/pseudonode.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/pseudonode_test.go b/agreement/pseudonode_test.go
index dfb9f2ce1..a60a1e5ef 100644
--- a/agreement/pseudonode_test.go
+++ b/agreement/pseudonode_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/router.go b/agreement/router.go
index f6f0ea010..040fd525e 100644
--- a/agreement/router.go
+++ b/agreement/router.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/selector.go b/agreement/selector.go
index d19cfef23..505d54188 100644
--- a/agreement/selector.go
+++ b/agreement/selector.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/service.go b/agreement/service.go
index 00b192b5c..3cac12619 100644
--- a/agreement/service.go
+++ b/agreement/service.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -88,7 +88,7 @@ type externalDemuxSignals struct {
// MakeService creates a new Agreement Service instance given a set of Parameters.
//
// Call Start to start execution and Shutdown to finish execution.
-func MakeService(p Parameters) *Service {
+func MakeService(p Parameters) (*Service, error) {
s := new(Service)
s.parameters = parameters(p)
@@ -97,12 +97,16 @@ func MakeService(p Parameters) *Service {
// GOAL2-541: tracer is not concurrency safe. It should only ever be
// accessed by main state machine loop.
- s.tracer = makeTracer(s.log, defaultCadaverName, p.CadaverSizeTarget,
+ var err error
+ s.tracer, err = makeTracer(s.log, defaultCadaverName, p.CadaverSizeTarget, p.CadaverDirectory,
s.Local.EnableAgreementReporting, s.Local.EnableAgreementTimeMetrics)
+ if err != nil {
+ return nil, err
+ }
s.persistenceLoop = makeAsyncPersistenceLoop(s.log, s.Accessor, s.Ledger)
- return s
+ return s, nil
}
// SetTracerFilename updates the tracer filename used.
diff --git a/agreement/service_test.go b/agreement/service_test.go
index b5ec2ce61..2cf696962 100644
--- a/agreement/service_test.go
+++ b/agreement/service_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -752,7 +752,8 @@ func setupAgreementWithValidator(t *testing.T, numNodes int, traceLevel traceLev
os.Remove(cadaverFilename + ".cdv")
os.Remove(cadaverFilename + ".cdv.archive")
- services[i] = MakeService(params)
+ services[i], err = MakeService(params)
+ require.NoError(t, err)
services[i].tracer.cadaver.baseFilename = cadaverFilename
services[i].tracer.level = traceLevel
services[i].tracer.tag = strconv.Itoa(i)
diff --git a/agreement/sort.go b/agreement/sort.go
index 21fa3fc27..f95060ca8 100644
--- a/agreement/sort.go
+++ b/agreement/sort.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/sort_test.go b/agreement/sort_test.go
index 8240e5eff..be2b968dd 100644
--- a/agreement/sort_test.go
+++ b/agreement/sort_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/state_machine_test.go b/agreement/state_machine_test.go
index 8518ff70d..0effd9fda 100644
--- a/agreement/state_machine_test.go
+++ b/agreement/state_machine_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/trace.go b/agreement/trace.go
index a3f8cea66..d5aeb3d9d 100644
--- a/agreement/trace.go
+++ b/agreement/trace.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -74,7 +74,7 @@ type tracer struct {
const cadaverSizeMinimum = 100 * 1024 // 100 KB
-func makeTracer(log serviceLogger, cadaverFilename string, cadaverSizeTarget uint64, verboseReportFlag bool, timingReportFlag bool) *tracer {
+func makeTracer(log serviceLogger, cadaverFilename string, cadaverSizeTarget uint64, cadaverDirectory string, verboseReportFlag bool, timingReportFlag bool) (*tracer, error) {
t := new(tracer)
t.log = log
t.verboseReports = verboseReportFlag
@@ -85,15 +85,20 @@ func makeTracer(log serviceLogger, cadaverFilename string, cadaverSizeTarget uin
if fileSizeTarget == 0 {
// disabled
} else if fileSizeTarget < 0 {
- log.Errorf("agreement: cadaver filesize too large: int64(%v) < 0", cadaverSizeTarget)
+ return nil, fmt.Errorf("agreement: cadaver filesize too large: int64(%v) < 0", cadaverSizeTarget)
} else if fileSizeTarget < cadaverSizeMinimum {
- log.Errorf("agreement: cadaver filesize too small: %v < %v", fileSizeTarget, cadaverSizeMinimum)
+ return nil, fmt.Errorf("agreement: cadaver filesize too small: %v < %v", fileSizeTarget, cadaverSizeMinimum)
} else if fileSizeTarget > 0 {
t.cadaver.baseFilename = cadaverFilename
+ t.cadaver.baseDirectory = cadaverDirectory
t.cadaver.fileSizeTarget = fileSizeTarget
- log.Infof("agreement: cadaver set to %v", cadaverFilename)
+ log.Infof("agreement: cadaver set to %s", t.cadaver.filename())
+ err := t.cadaver.init()
+ if err != nil {
+ return nil, err
+ }
}
- return t
+ return t, nil
}
// call this method to setup timing generators before entering target round, pipelining properly.
diff --git a/agreement/traceTime.go b/agreement/traceTime.go
index 08d5bdb25..d397a66fb 100644
--- a/agreement/traceTime.go
+++ b/agreement/traceTime.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/types.go b/agreement/types.go
index 51793aa07..450570c62 100644
--- a/agreement/types.go
+++ b/agreement/types.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/vote.go b/agreement/vote.go
index e39edd7b7..af157ee3b 100644
--- a/agreement/vote.go
+++ b/agreement/vote.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteAggregator.go b/agreement/voteAggregator.go
index 196e91a3b..29d5e2b54 100644
--- a/agreement/voteAggregator.go
+++ b/agreement/voteAggregator.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteAggregatorContract.go b/agreement/voteAggregatorContract.go
index 843746fab..5ebdb5692 100644
--- a/agreement/voteAggregatorContract.go
+++ b/agreement/voteAggregatorContract.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteAggregator_test.go b/agreement/voteAggregator_test.go
index 8795a0b36..554426ecb 100644
--- a/agreement/voteAggregator_test.go
+++ b/agreement/voteAggregator_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteAuxiliary.go b/agreement/voteAuxiliary.go
index d99a01139..d560c4f01 100644
--- a/agreement/voteAuxiliary.go
+++ b/agreement/voteAuxiliary.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteAuxiliaryContract.go b/agreement/voteAuxiliaryContract.go
index e8b501808..ada331a6e 100644
--- a/agreement/voteAuxiliaryContract.go
+++ b/agreement/voteAuxiliaryContract.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteAuxiliary_test.go b/agreement/voteAuxiliary_test.go
index 667c3a147..c20fecc62 100644
--- a/agreement/voteAuxiliary_test.go
+++ b/agreement/voteAuxiliary_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteTracker.go b/agreement/voteTracker.go
index 394584015..f1250566a 100644
--- a/agreement/voteTracker.go
+++ b/agreement/voteTracker.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteTrackerContract.go b/agreement/voteTrackerContract.go
index ad1585e6d..095523097 100644
--- a/agreement/voteTrackerContract.go
+++ b/agreement/voteTrackerContract.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/voteTracker_test.go b/agreement/voteTracker_test.go
index 7295659d8..75359fa34 100644
--- a/agreement/voteTracker_test.go
+++ b/agreement/voteTracker_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/agreement/vote_test.go b/agreement/vote_test.go
index d3f6f39bf..22b1049be 100644
--- a/agreement/vote_test.go
+++ b/agreement/vote_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/buildnumber.dat b/buildnumber.dat
index 00750edc0..0cfbf0888 100644
--- a/buildnumber.dat
+++ b/buildnumber.dat
@@ -1 +1 @@
-3
+2
diff --git a/catchup/catchpointService.go b/catchup/catchpointService.go
index 41c75172e..974b76ef9 100644
--- a/catchup/catchpointService.go
+++ b/catchup/catchpointService.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/catchpointService_test.go b/catchup/catchpointService_test.go
index 02f4a9b7a..de91b456e 100644
--- a/catchup/catchpointService_test.go
+++ b/catchup/catchpointService_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/fetcher_test.go b/catchup/fetcher_test.go
index 3c83baae0..df762bc43 100644
--- a/catchup/fetcher_test.go
+++ b/catchup/fetcher_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/ledgerFetcher.go b/catchup/ledgerFetcher.go
index 30c5ccb3b..bf79c5d3b 100644
--- a/catchup/ledgerFetcher.go
+++ b/catchup/ledgerFetcher.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -30,6 +30,7 @@ import (
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/ledger"
+ "github.com/algorand/go-algorand/ledger/encoded"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/network"
"github.com/algorand/go-algorand/rpcs"
@@ -40,7 +41,7 @@ var errNoLedgerForRound = errors.New("no ledger available for given round")
const (
// maxCatchpointFileChunkSize is a rough estimate for the worst-case scenario we're going to have of all the accounts data per a single catchpoint file chunk and one account with max resources.
- maxCatchpointFileChunkSize = ledger.BalancesPerCatchpointFileChunk*(ledger.MaxEncodedBaseAccountDataSize+ledger.MaxEncodedKVDataSize) + ledger.ResourcesPerCatchpointFileChunk*ledger.MaxEncodedBaseResourceDataSize
+ maxCatchpointFileChunkSize = ledger.BalancesPerCatchpointFileChunk*(ledger.MaxEncodedBaseAccountDataSize+encoded.MaxEncodedKVDataSize) + ledger.ResourcesPerCatchpointFileChunk*ledger.MaxEncodedBaseResourceDataSize
// defaultMinCatchpointFileDownloadBytesPerSecond defines the worst-case scenario download speed we expect to get while downloading a catchpoint file
defaultMinCatchpointFileDownloadBytesPerSecond = 20 * 1024
// catchpointFileStreamReadSize defines the number of bytes we would attempt to read at each iteration from the incoming http data stream
diff --git a/catchup/ledgerFetcher_test.go b/catchup/ledgerFetcher_test.go
index 4cb57d7fd..248192c4f 100644
--- a/catchup/ledgerFetcher_test.go
+++ b/catchup/ledgerFetcher_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/networkFetcher.go b/catchup/networkFetcher.go
deleted file mode 100644
index d82395e8d..000000000
--- a/catchup/networkFetcher.go
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (C) 2019-2022 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 catchup
-
-import (
- "context"
- "errors"
- "fmt"
- "time"
-
- "github.com/algorand/go-algorand/agreement"
- "github.com/algorand/go-algorand/config"
- "github.com/algorand/go-algorand/data/basics"
- "github.com/algorand/go-algorand/data/bookkeeping"
- "github.com/algorand/go-algorand/logging"
- "github.com/algorand/go-algorand/network"
-)
-
-// NetworkFetcher is the struct used to export fetchBlock function from universalFetcher
-type NetworkFetcher struct {
- log logging.Logger
- cfg config.Local
- auth BlockAuthenticator
- peerSelector *peerSelector
- fetcher *universalBlockFetcher
-}
-
-// MakeNetworkFetcher initializes a NetworkFetcher service
-func MakeNetworkFetcher(log logging.Logger, net network.GossipNode, cfg config.Local, auth BlockAuthenticator, pipelineFetch bool) *NetworkFetcher {
- netFetcher := &NetworkFetcher{
- log: log,
- cfg: cfg,
- auth: auth,
- peerSelector: createPeerSelector(net, cfg, pipelineFetch),
- fetcher: makeUniversalBlockFetcher(log, net, cfg),
- }
- return netFetcher
-}
-
-func (netFetcher *NetworkFetcher) getHTTPPeer() (network.HTTPPeer, *peerSelectorPeer, error) {
- for retryCount := 0; retryCount < netFetcher.cfg.CatchupBlockDownloadRetryAttempts; retryCount++ {
- psp, err := netFetcher.peerSelector.getNextPeer()
- if err != nil {
- if err != errPeerSelectorNoPeerPoolsAvailable {
- err = fmt.Errorf("FetchBlock: unable to obtain a list of peers to download the block from : %w", err)
- return nil, nil, err
- }
- // this is a possible on startup, since the network package might have yet to retrieve the list of peers.
- netFetcher.log.Infof("FetchBlock: unable to obtain a list of peers to download the block from; will retry shortly.")
- time.Sleep(noPeersAvailableSleepInterval)
- continue
- }
- peer := psp.Peer
- httpPeer, ok := peer.(network.HTTPPeer)
- if ok {
- return httpPeer, psp, nil
- }
- netFetcher.log.Warnf("FetchBlock: non-HTTP peer was provided by the peer selector")
- netFetcher.peerSelector.rankPeer(psp, peerRankInvalidDownload)
- }
- return nil, nil, errors.New("FetchBlock: recurring non-HTTP peer was provided by the peer selector")
-}
-
-// FetchBlock function given a round number returns a block from a http peer
-func (netFetcher *NetworkFetcher) FetchBlock(ctx context.Context, round basics.Round) (*bookkeeping.Block,
- *agreement.Certificate, time.Duration, error) {
- // internal retry attempt to fetch the block
- for retryCount := 0; retryCount < netFetcher.cfg.CatchupBlockDownloadRetryAttempts; retryCount++ {
- httpPeer, psp, err := netFetcher.getHTTPPeer()
- if err != nil {
- return nil, nil, time.Duration(0), err
- }
-
- blk, cert, downloadDuration, err := netFetcher.fetcher.fetchBlock(ctx, round, httpPeer)
- if err != nil {
- if ctx.Err() != nil {
- // caller of the function decided to cancel the download
- return nil, nil, time.Duration(0), err
- }
- netFetcher.log.Infof("FetchBlock: failed to download block %d on attempt %d out of %d. %v",
- round, retryCount+1, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, err)
- netFetcher.peerSelector.rankPeer(psp, peerRankDownloadFailed)
- continue // retry the fetch
- }
-
- // Check that the block's contents match the block header
- if !blk.ContentsMatchHeader() && blk.Round() > 0 {
- netFetcher.peerSelector.rankPeer(psp, peerRankInvalidDownload)
- // Check if this mismatch is due to an unsupported protocol version
- if _, ok := config.Consensus[blk.BlockHeader.CurrentProtocol]; !ok {
- netFetcher.log.Errorf("FetchBlock: downloaded block(%v) unsupported protocol version detected: '%v'",
- round, blk.BlockHeader.CurrentProtocol)
- }
- netFetcher.log.Warnf("FetchBlock: downloaded block(%v) contents do not match header", round)
- netFetcher.log.Infof("FetchBlock: failed to download block %d on attempt %d out of %d. %v",
- round, retryCount+1, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, err)
- continue // retry the fetch
- }
-
- // Authenticate the block. for correct execution, caller should call FetchBlock only when the lookback block is available
- if netFetcher.cfg.CatchupVerifyCertificate() {
- err = netFetcher.auth.Authenticate(blk, cert)
- if err != nil {
- netFetcher.log.Warnf("FetchBlock: cert authenticatation failed for block %d on attempt %d out of %d. %v",
- round, retryCount+1, netFetcher.cfg.CatchupBlockDownloadRetryAttempts, err)
- netFetcher.peerSelector.rankPeer(psp, peerRankInvalidDownload)
- continue // retry the fetch
- }
- }
-
- // upon successful download rank the peer according to the download speed
- peerRank := netFetcher.peerSelector.peerDownloadDurationToRank(psp, downloadDuration)
- netFetcher.peerSelector.rankPeer(psp, peerRank)
- return blk, cert, downloadDuration, err
-
- }
- err := fmt.Errorf("FetchBlock failed after multiple blocks download attempts: %v unsuccessful attempts",
- netFetcher.cfg.CatchupBlockDownloadRetryAttempts)
- return nil, nil, time.Duration(0), err
-}
diff --git a/catchup/networkFetcher_test.go b/catchup/networkFetcher_test.go
deleted file mode 100644
index 7c6a2c885..000000000
--- a/catchup/networkFetcher_test.go
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright (C) 2019-2022 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 catchup
-
-import (
- "context"
- "sync"
- "testing"
-
- "github.com/algorand/go-algorand/config"
- "github.com/algorand/go-algorand/data/bookkeeping"
- "github.com/algorand/go-algorand/logging"
- "github.com/algorand/go-algorand/rpcs"
- "github.com/algorand/go-algorand/test/partitiontest"
- "github.com/stretchr/testify/require"
-)
-
-func TestFetchBlock(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- ledger, next, b, err := buildTestLedger(t, bookkeeping.Block{})
- if err != nil {
- t.Fatal(err)
- return
- }
-
- blockServiceConfig := config.GetDefaultLocal()
- blockServiceConfig.EnableBlockService = true
- blockServiceConfig.EnableBlockServiceFallbackToArchiver = false
-
- net := &httpTestPeerSource{}
- ls := rpcs.MakeBlockService(logging.Base(), blockServiceConfig, ledger, net, "test genesisID")
-
- node := basicRPCNode{}
- node.RegisterHTTPHandler(rpcs.BlockServiceBlockPath, ls)
- node.start()
- defer node.stop()
- rootURL := node.rootURL()
-
- net.addPeer(rootURL)
-
- // Disable block authentication
- cfg := config.GetDefaultLocal()
- cfg.CatchupBlockValidateMode = 1
- fetcher := MakeNetworkFetcher(logging.TestingLog(t), net, cfg, nil, false)
-
- block, _, duration, err := fetcher.FetchBlock(context.Background(), next)
-
- require.NoError(t, err)
- require.Equal(t, &b, block)
- require.GreaterOrEqual(t, int64(duration), int64(0))
-
- block, cert, duration, err := fetcher.FetchBlock(context.Background(), next+1)
-
- require.Error(t, errNoBlockForRound, err)
- require.Contains(t, err.Error(), "FetchBlock failed after multiple blocks download attempts")
- require.Nil(t, block)
- require.Nil(t, cert)
- require.Equal(t, int64(duration), int64(0))
-}
-
-func TestConcurrentAttemptsToFetchBlockSuccess(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- ledger, next, b, err := buildTestLedger(t, bookkeeping.Block{})
- if err != nil {
- t.Fatal(err)
- return
- }
-
- blockServiceConfig := config.GetDefaultLocal()
- blockServiceConfig.EnableBlockService = true
- blockServiceConfig.EnableBlockServiceFallbackToArchiver = false
-
- net := &httpTestPeerSource{}
- ls := rpcs.MakeBlockService(logging.Base(), blockServiceConfig, ledger, net, "test genesisID")
-
- node := basicRPCNode{}
- node.RegisterHTTPHandler(rpcs.BlockServiceBlockPath, ls)
- node.start()
- defer node.stop()
- rootURL := node.rootURL()
-
- net.addPeer(rootURL)
-
- // Disable block authentication
- cfg := config.GetDefaultLocal()
- cfg.CatchupBlockValidateMode = 1
- fetcher := MakeNetworkFetcher(logging.TestingLog(t), net, cfg, nil, false)
-
- // start is used to synchronize concurrent fetchBlock attempts
- // parallelRequests represents number of concurrent attempts
- start := make(chan struct{})
- parallelRequests := int(cfg.CatchupParallelBlocks)
- var wg sync.WaitGroup
- wg.Add(parallelRequests)
- for i := 0; i < parallelRequests; i++ {
- go func() {
- <-start
- block, _, duration, err := fetcher.FetchBlock(context.Background(), next)
- require.NoError(t, err)
- require.Equal(t, &b, block)
- require.GreaterOrEqual(t, int64(duration), int64(0))
- wg.Done()
- }()
- }
- close(start)
- wg.Wait()
-}
-
-func TestHTTPPeerNotAvailable(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- net := &httpTestPeerSource{}
-
- // Disable block authentication
- cfg := config.GetDefaultLocal()
- cfg.CatchupBlockValidateMode = 1
- cfg.CatchupBlockDownloadRetryAttempts = 1
-
- fetcher := MakeNetworkFetcher(logging.TestingLog(t), net, cfg, nil, false)
-
- _, _, _, err := fetcher.FetchBlock(context.Background(), 1)
- require.Contains(t, err.Error(), "recurring non-HTTP peer was provided by the peer selector")
-}
-
-func TestFetchBlockFailed(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- net := &httpTestPeerSource{}
- wsPeer := makeTestUnicastPeer(net, t)
- net.addPeer(wsPeer.GetAddress())
-
- // Disable block authentication
- cfg := config.GetDefaultLocal()
- cfg.CatchupBlockValidateMode = 1
- cfg.CatchupBlockDownloadRetryAttempts = 1
-
- fetcher := MakeNetworkFetcher(logging.TestingLog(t), net, cfg, nil, false)
-
- _, _, _, err := fetcher.FetchBlock(context.Background(), 1)
- require.Contains(t, err.Error(), "FetchBlock failed after multiple blocks download attempts")
-}
-
-func TestFetchBlockAuthenticationFailed(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- ledger, next, _, err := buildTestLedger(t, bookkeeping.Block{})
- if err != nil {
- t.Fatal(err)
- return
- }
-
- blockServiceConfig := config.GetDefaultLocal()
- blockServiceConfig.EnableBlockService = true
- blockServiceConfig.EnableBlockServiceFallbackToArchiver = false
-
- net := &httpTestPeerSource{}
- ls := rpcs.MakeBlockService(logging.Base(), blockServiceConfig, ledger, net, "test genesisID")
-
- node := basicRPCNode{}
- node.RegisterHTTPHandler(rpcs.BlockServiceBlockPath, ls)
- node.start()
- defer node.stop()
- rootURL := node.rootURL()
-
- net.addPeer(rootURL)
-
- cfg := config.GetDefaultLocal()
- cfg.CatchupBlockDownloadRetryAttempts = 1
-
- fetcher := MakeNetworkFetcher(logging.TestingLog(t), net, cfg, &mockedAuthenticator{errorRound: int(next)}, false)
-
- _, _, _, err = fetcher.FetchBlock(context.Background(), next)
- require.Contains(t, err.Error(), "FetchBlock failed after multiple blocks download attempts")
-}
diff --git a/catchup/peerSelector.go b/catchup/peerSelector.go
index 822d0b709..b78d802fa 100644
--- a/catchup/peerSelector.go
+++ b/catchup/peerSelector.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/peerSelector_test.go b/catchup/peerSelector_test.go
index c10298a51..8e66b9ab8 100644
--- a/catchup/peerSelector_test.go
+++ b/catchup/peerSelector_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/pref_test.go b/catchup/pref_test.go
index 4a56156e1..688958ab2 100644
--- a/catchup/pref_test.go
+++ b/catchup/pref_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/service.go b/catchup/service.go
index 919822825..2204212df 100644
--- a/catchup/service.go
+++ b/catchup/service.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/service_test.go b/catchup/service_test.go
index efa341c34..4d1bafb28 100644
--- a/catchup/service_test.go
+++ b/catchup/service_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/universalFetcher.go b/catchup/universalFetcher.go
index 4527c2f5e..789ce55c7 100644
--- a/catchup/universalFetcher.go
+++ b/catchup/universalFetcher.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/catchup/universalFetcher_test.go b/catchup/universalFetcher_test.go
index 49b921dd4..6107a695f 100644
--- a/catchup/universalFetcher_test.go
+++ b/catchup/universalFetcher_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algocfg/datadir.go b/cmd/algocfg/datadir.go
index 99ca1a21b..93aa4d7a1 100644
--- a/cmd/algocfg/datadir.go
+++ b/cmd/algocfg/datadir.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algocfg/getCommand.go b/cmd/algocfg/getCommand.go
index c22ff597c..3ad9a1794 100644
--- a/cmd/algocfg/getCommand.go
+++ b/cmd/algocfg/getCommand.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -46,7 +46,7 @@ var getCmd = &cobra.Command{
onDataDirs(func(dataDir string) {
cfg, err := config.LoadConfigFromDisk(dataDir)
if err != nil && !os.IsNotExist(err) {
- reportWarnf("Error loading config file from '%s'", dataDir)
+ reportWarnf("Error loading config file from '%s' - %s", dataDir, err)
anyError = true
return
}
diff --git a/cmd/algocfg/getCommand_test.go b/cmd/algocfg/getCommand_test.go
index 0547c1ccc..6c1285bb7 100644
--- a/cmd/algocfg/getCommand_test.go
+++ b/cmd/algocfg/getCommand_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algocfg/main.go b/cmd/algocfg/main.go
index 0e38511f8..d3ee5c8a9 100644
--- a/cmd/algocfg/main.go
+++ b/cmd/algocfg/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algocfg/messages.go b/cmd/algocfg/messages.go
index e0e27aa3a..5225cfc86 100644
--- a/cmd/algocfg/messages.go
+++ b/cmd/algocfg/messages.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algocfg/report.go b/cmd/algocfg/report.go
index 219f84070..a688a04ed 100644
--- a/cmd/algocfg/report.go
+++ b/cmd/algocfg/report.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algocfg/resetCommand.go b/cmd/algocfg/resetCommand.go
index 187afe93c..6e57bb232 100644
--- a/cmd/algocfg/resetCommand.go
+++ b/cmd/algocfg/resetCommand.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -48,7 +48,7 @@ var resetCmd = &cobra.Command{
onDataDirs(func(dataDir string) {
cfg, err := config.LoadConfigFromDisk(dataDir)
if err != nil && !os.IsNotExist(err) {
- reportWarnf("Error loading config file from '%s'", dataDir)
+ reportWarnf("Error loading config file from '%s' - %s", dataDir, err)
anyError = true
return
}
diff --git a/cmd/algocfg/setCommand.go b/cmd/algocfg/setCommand.go
index b5a4e6c1b..ac1d7c6ea 100644
--- a/cmd/algocfg/setCommand.go
+++ b/cmd/algocfg/setCommand.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -52,7 +52,7 @@ var setCmd = &cobra.Command{
onDataDirs(func(dataDir string) {
cfg, err := config.LoadConfigFromDisk(dataDir)
if err != nil && !os.IsNotExist(err) {
- reportWarnf("Error loading config file from '%s'", dataDir)
+ reportWarnf("Error loading config file from '%s' - %s", dataDir, err)
anyError = true
return
}
diff --git a/cmd/algod/main.go b/cmd/algod/main.go
index b67747984..cb0960e61 100644
--- a/cmd/algod/main.go
+++ b/cmd/algod/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -344,7 +344,17 @@ func run() int {
// Send a heartbeat event every 10 minutes as a sign of life
go func() {
- ticker := time.NewTicker(10 * time.Minute)
+ var interval time.Duration
+ defaultIntervalSecs := config.GetDefaultLocal().HeartbeatUpdateInterval
+ switch {
+ case cfg.HeartbeatUpdateInterval <= 0: // use default
+ interval = time.Second * time.Duration(defaultIntervalSecs)
+ case cfg.HeartbeatUpdateInterval < 60: // min frequency 1 minute
+ interval = time.Minute
+ default:
+ interval = time.Second * time.Duration(cfg.HeartbeatUpdateInterval)
+ }
+ ticker := time.NewTicker(interval)
defer ticker.Stop()
sendHeartbeat := func() {
diff --git a/cmd/algod/main_test.go b/cmd/algod/main_test.go
index c25505167..e2a37a8f8 100644
--- a/cmd/algod/main_test.go
+++ b/cmd/algod/main_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algofix/deadlock.go b/cmd/algofix/deadlock.go
index b7342235e..c6a975764 100644
--- a/cmd/algofix/deadlock.go
+++ b/cmd/algofix/deadlock.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algofix/deadlock_test.go b/cmd/algofix/deadlock_test.go
index 720bafa10..426b4297f 100644
--- a/cmd/algofix/deadlock_test.go
+++ b/cmd/algofix/deadlock_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/blockWatcher.go b/cmd/algoh/blockWatcher.go
index 43da894d0..bcfb519a8 100644
--- a/cmd/algoh/blockWatcher.go
+++ b/cmd/algoh/blockWatcher.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/blockWatcher_test.go b/cmd/algoh/blockWatcher_test.go
index 1e4db0573..a62389729 100644
--- a/cmd/algoh/blockWatcher_test.go
+++ b/cmd/algoh/blockWatcher_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/blockstats.go b/cmd/algoh/blockstats.go
index 590cfb435..fd54bdff4 100644
--- a/cmd/algoh/blockstats.go
+++ b/cmd/algoh/blockstats.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/blockstats_test.go b/cmd/algoh/blockstats_test.go
index 0398404dd..2c58e8c43 100644
--- a/cmd/algoh/blockstats_test.go
+++ b/cmd/algoh/blockstats_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/client.go b/cmd/algoh/client.go
index 6d25759ec..a0a12194d 100644
--- a/cmd/algoh/client.go
+++ b/cmd/algoh/client.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/deadman.go b/cmd/algoh/deadman.go
index a15046f07..6b0e0e1bf 100644
--- a/cmd/algoh/deadman.go
+++ b/cmd/algoh/deadman.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/eventsender.go b/cmd/algoh/eventsender.go
index 64ed53eae..678dd203a 100644
--- a/cmd/algoh/eventsender.go
+++ b/cmd/algoh/eventsender.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/main.go b/cmd/algoh/main.go
index 4e75ae837..bcca7d567 100644
--- a/cmd/algoh/main.go
+++ b/cmd/algoh/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algoh/mockClient.go b/cmd/algoh/mockClient.go
index ce532a4f4..45e081e1e 100644
--- a/cmd/algoh/mockClient.go
+++ b/cmd/algoh/mockClient.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/common.go b/cmd/algokey/common.go
index 9362fab74..71b58498c 100644
--- a/cmd/algokey/common.go
+++ b/cmd/algokey/common.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/export.go b/cmd/algokey/export.go
index 2b0027580..2966eabd1 100644
--- a/cmd/algokey/export.go
+++ b/cmd/algokey/export.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/generate.go b/cmd/algokey/generate.go
index a52011699..4d4273585 100644
--- a/cmd/algokey/generate.go
+++ b/cmd/algokey/generate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/import.go b/cmd/algokey/import.go
index 6f17cd06a..34f90ca2e 100644
--- a/cmd/algokey/import.go
+++ b/cmd/algokey/import.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/keyreg.go b/cmd/algokey/keyreg.go
index 156697e7f..02f18649b 100644
--- a/cmd/algokey/keyreg.go
+++ b/cmd/algokey/keyreg.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/main.go b/cmd/algokey/main.go
index 450253e56..c04b73a3c 100644
--- a/cmd/algokey/main.go
+++ b/cmd/algokey/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/multisig.go b/cmd/algokey/multisig.go
index b6d0bb108..ef51d5dac 100644
--- a/cmd/algokey/multisig.go
+++ b/cmd/algokey/multisig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/part.go b/cmd/algokey/part.go
index 3fb7e49c2..034b9821a 100644
--- a/cmd/algokey/part.go
+++ b/cmd/algokey/part.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algokey/sign.go b/cmd/algokey/sign.go
index 14f14e58b..3d8bf6dbc 100644
--- a/cmd/algokey/sign.go
+++ b/cmd/algokey/sign.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algons/commands.go b/cmd/algons/commands.go
index 522d0b808..15469dd2b 100644
--- a/cmd/algons/commands.go
+++ b/cmd/algons/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algons/dnsCmd.go b/cmd/algons/dnsCmd.go
index a1771414c..10b4c1888 100644
--- a/cmd/algons/dnsCmd.go
+++ b/cmd/algons/dnsCmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algorelay/commands.go b/cmd/algorelay/commands.go
index ebc047854..8199c742c 100644
--- a/cmd/algorelay/commands.go
+++ b/cmd/algorelay/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algorelay/eb/eb.go b/cmd/algorelay/eb/eb.go
index 7bd635ad7..c03ef85b1 100644
--- a/cmd/algorelay/eb/eb.go
+++ b/cmd/algorelay/eb/eb.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/algorelay/relayCmd.go b/cmd/algorelay/relayCmd.go
index ff281313b..0d9748575 100644
--- a/cmd/algorelay/relayCmd.go
+++ b/cmd/algorelay/relayCmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/buildtools/commands.go b/cmd/buildtools/commands.go
index 4f71804d3..92a73124c 100644
--- a/cmd/buildtools/commands.go
+++ b/cmd/buildtools/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/buildtools/genesis.go b/cmd/buildtools/genesis.go
index e1aab257a..19712d423 100644
--- a/cmd/buildtools/genesis.go
+++ b/cmd/buildtools/genesis.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/catchpointdump/commands.go b/cmd/catchpointdump/commands.go
index e06b0b79a..82deb897f 100644
--- a/cmd/catchpointdump/commands.go
+++ b/cmd/catchpointdump/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/catchpointdump/database.go b/cmd/catchpointdump/database.go
index 227ff7a6a..29c05e78d 100644
--- a/cmd/catchpointdump/database.go
+++ b/cmd/catchpointdump/database.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/catchpointdump/file.go b/cmd/catchpointdump/file.go
index 97766ff97..79a426c0a 100644
--- a/cmd/catchpointdump/file.go
+++ b/cmd/catchpointdump/file.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -32,11 +32,11 @@ import (
"github.com/spf13/cobra"
+ "github.com/algorand/avm-abi/apps"
cmdutil "github.com/algorand/go-algorand/cmd/util"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
- "github.com/algorand/go-algorand/data/transactions/logic"
"github.com/algorand/go-algorand/ledger"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store"
@@ -436,7 +436,7 @@ func printAccountsDatabase(databaseName string, stagingTables bool, fileHeader l
func printKeyValue(writer *bufio.Writer, key, value []byte) {
var pretty string
- ai, rest, err := logic.SplitBoxKey(string(key))
+ ai, rest, err := apps.SplitBoxKey(string(key))
if err == nil {
pretty = fmt.Sprintf("box(%d, %s)", ai, base64.StdEncoding.EncodeToString([]byte(rest)))
} else {
diff --git a/cmd/catchpointdump/net.go b/cmd/catchpointdump/net.go
index e63afb187..d30cfc297 100644
--- a/cmd/catchpointdump/net.go
+++ b/cmd/catchpointdump/net.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/catchupsrv/download.go b/cmd/catchupsrv/download.go
index 6a5880d42..af4d7df6b 100644
--- a/cmd/catchupsrv/download.go
+++ b/cmd/catchupsrv/download.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/catchupsrv/download_test.go b/cmd/catchupsrv/download_test.go
index b616f81f5..f0f911ed1 100644
--- a/cmd/catchupsrv/download_test.go
+++ b/cmd/catchupsrv/download_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/catchupsrv/main.go b/cmd/catchupsrv/main.go
index 1f0de542a..f89c69ad0 100644
--- a/cmd/catchupsrv/main.go
+++ b/cmd/catchupsrv/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/catchupsrv/tarblocks.go b/cmd/catchupsrv/tarblocks.go
index 25f686f0c..d5957d5a2 100644
--- a/cmd/catchupsrv/tarblocks.go
+++ b/cmd/catchupsrv/tarblocks.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/dbgen/main.go b/cmd/dbgen/main.go
index f73809ed7..0bcc3e93c 100644
--- a/cmd/dbgen/main.go
+++ b/cmd/dbgen/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/diagcfg/main.go b/cmd/diagcfg/main.go
index e2f1003d4..6953daf78 100644
--- a/cmd/diagcfg/main.go
+++ b/cmd/diagcfg/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/diagcfg/messages.go b/cmd/diagcfg/messages.go
index 70898a043..45acf2e3d 100644
--- a/cmd/diagcfg/messages.go
+++ b/cmd/diagcfg/messages.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/diagcfg/metric.go b/cmd/diagcfg/metric.go
index 4fefd69b7..d5c44189a 100644
--- a/cmd/diagcfg/metric.go
+++ b/cmd/diagcfg/metric.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/diagcfg/telemetry.go b/cmd/diagcfg/telemetry.go
index e49e0c215..fac9bb46f 100644
--- a/cmd/diagcfg/telemetry.go
+++ b/cmd/diagcfg/telemetry.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/dispenser/server.go b/cmd/dispenser/server.go
index 7890ab8f4..064d9f58e 100644
--- a/cmd/dispenser/server.go
+++ b/cmd/dispenser/server.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/genesis/newgenesis.go b/cmd/genesis/newgenesis.go
index d86d67df3..25d5f92d8 100644
--- a/cmd/genesis/newgenesis.go
+++ b/cmd/genesis/newgenesis.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/account.go b/cmd/goal/account.go
index 6598422aa..792d39b09 100644
--- a/cmd/goal/account.go
+++ b/cmd/goal/account.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/accountsList.go b/cmd/goal/accountsList.go
index 366a132c6..390d3b2b0 100644
--- a/cmd/goal/accountsList.go
+++ b/cmd/goal/accountsList.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/application.go b/cmd/goal/application.go
index e58598bcc..e01e1fdee 100644
--- a/cmd/goal/application.go
+++ b/cmd/goal/application.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -31,6 +31,7 @@ import (
"github.com/spf13/cobra"
"github.com/algorand/avm-abi/abi"
+ "github.com/algorand/avm-abi/apps"
"github.com/algorand/go-algorand/crypto"
apiclient "github.com/algorand/go-algorand/daemon/algod/api/client"
"github.com/algorand/go-algorand/data/basics"
@@ -198,8 +199,8 @@ func panicIfErr(err error) {
}
}
-func newAppCallBytes(arg string) logic.AppCallBytes {
- appBytes, err := logic.NewAppCallBytes(arg)
+func newAppCallBytes(arg string) apps.AppCallBytes {
+ appBytes, err := apps.NewAppCallBytes(arg)
if err != nil {
reportErrorf(err.Error())
}
@@ -207,16 +208,16 @@ func newAppCallBytes(arg string) logic.AppCallBytes {
}
type appCallInputs struct {
- Accounts []string `codec:"accounts"`
- ForeignApps []uint64 `codec:"foreignapps"`
- ForeignAssets []uint64 `codec:"foreignassets"`
- Boxes []boxRef `codec:"boxes"`
- Args []logic.AppCallBytes `codec:"args"`
+ Accounts []string `codec:"accounts"`
+ ForeignApps []uint64 `codec:"foreignapps"`
+ ForeignAssets []uint64 `codec:"foreignassets"`
+ Boxes []boxRef `codec:"boxes"`
+ Args []apps.AppCallBytes `codec:"args"`
}
type boxRef struct {
- appID uint64 `codec:"app"`
- name logic.AppCallBytes `codec:"name"`
+ appID uint64 `codec:"app"`
+ name apps.AppCallBytes `codec:"name"`
}
// newBoxRef parses a command-line box ref, which is an optional appId, a comma,
@@ -335,7 +336,7 @@ func processAppInputFile() (args [][]byte, accounts []string, foreignApps []uint
return parseAppInputs(inputs)
}
-func getAppInputs() (args [][]byte, accounts []string, apps []uint64, assets []uint64, boxes []transactions.BoxRef) {
+func getAppInputs() (args [][]byte, accounts []string, _ []uint64, assets []uint64, boxes []transactions.BoxRef) {
if appInputFilename != "" {
if appArgs != nil || appStrAccounts != nil || foreignApps != nil || foreignAssets != nil {
reportErrorf("Cannot specify both command-line arguments/resources and JSON input filename")
@@ -348,7 +349,7 @@ func getAppInputs() (args [][]byte, accounts []string, apps []uint64, assets []u
// on it. appArgs became `StringArrayVar` in order to support abi arguments
// which contain commas.
- var encodedArgs []logic.AppCallBytes
+ var encodedArgs []apps.AppCallBytes
for _, arg := range appArgs {
if len(arg) > 0 {
encodedArgs = append(encodedArgs, newAppCallBytes(arg))
diff --git a/cmd/goal/application_test.go b/cmd/goal/application_test.go
index 7de23a5be..687518fe0 100644
--- a/cmd/goal/application_test.go
+++ b/cmd/goal/application_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/asset.go b/cmd/goal/asset.go
index fdf5a61a2..97606c4cb 100644
--- a/cmd/goal/asset.go
+++ b/cmd/goal/asset.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/box.go b/cmd/goal/box.go
index f603b043d..4fd6495fa 100644
--- a/cmd/goal/box.go
+++ b/cmd/goal/box.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/clerk.go b/cmd/goal/clerk.go
index 84aef5e69..dce74a81d 100644
--- a/cmd/goal/clerk.go
+++ b/cmd/goal/clerk.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -422,7 +422,7 @@ var sendCmd = &cobra.Command{
CurrentProtocol: proto,
},
}
- groupCtx, err := verify.PrepareGroupContext([]transactions.SignedTxn{uncheckedTxn}, blockHeader, nil)
+ groupCtx, err := verify.PrepareGroupContext([]transactions.SignedTxn{uncheckedTxn}, &blockHeader, nil)
if err == nil {
err = verify.LogicSigSanityCheck(&uncheckedTxn, 0, groupCtx)
}
@@ -823,7 +823,7 @@ var signCmd = &cobra.Command{
}
var groupCtx *verify.GroupContext
if lsig.Logic != nil {
- groupCtx, err = verify.PrepareGroupContext(txnGroup, contextHdr, nil)
+ groupCtx, err = verify.PrepareGroupContext(txnGroup, &contextHdr, nil)
if err != nil {
// this error has to be unsupported protocol
reportErrorf("%s: %v", txFilename, err)
@@ -962,7 +962,7 @@ func assembleFileImpl(fname string, printWarnings bool) *logic.OpStream {
}
ops, err := logic.AssembleString(string(text))
if err != nil {
- ops.ReportProblems(fname, os.Stderr)
+ ops.ReportMultipleErrors(fname, os.Stderr)
reportErrorf("%s: %s", fname, err)
}
_, params := getProto(protoVersion)
diff --git a/cmd/goal/commands.go b/cmd/goal/commands.go
index 5440c04ec..ce17c9de4 100644
--- a/cmd/goal/commands.go
+++ b/cmd/goal/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/commands_test.go b/cmd/goal/commands_test.go
index 1d7864256..d83751331 100644
--- a/cmd/goal/commands_test.go
+++ b/cmd/goal/commands_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/common.go b/cmd/goal/common.go
index 893fcd176..3c4189dbf 100644
--- a/cmd/goal/common.go
+++ b/cmd/goal/common.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/completion.go b/cmd/goal/completion.go
index 235b1a238..418b1c857 100644
--- a/cmd/goal/completion.go
+++ b/cmd/goal/completion.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/formatting.go b/cmd/goal/formatting.go
index ed4b3116e..9eebf3fbb 100644
--- a/cmd/goal/formatting.go
+++ b/cmd/goal/formatting.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/formatting_test.go b/cmd/goal/formatting_test.go
index 9915597d7..f368c54c9 100644
--- a/cmd/goal/formatting_test.go
+++ b/cmd/goal/formatting_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -72,6 +72,7 @@ func TestNewBoxRef(t *testing.T) {
}
func TestStringsToBoxRefs(t *testing.T) {
+ partitiontest.PartitionTest(t)
brs := stringsToBoxRefs([]string{"77,str:hello", "55,int:6", "int:88"})
require.EqualValues(t, 77, brs[0].appID)
require.EqualValues(t, 55, brs[1].appID)
diff --git a/cmd/goal/inspect.go b/cmd/goal/inspect.go
index bdb11f434..9f8c078d1 100644
--- a/cmd/goal/inspect.go
+++ b/cmd/goal/inspect.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/inspect_test.go b/cmd/goal/inspect_test.go
index 2e97444b3..7f4eb7e1c 100644
--- a/cmd/goal/inspect_test.go
+++ b/cmd/goal/inspect_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/interact.go b/cmd/goal/interact.go
index 8777163e7..000b7e845 100644
--- a/cmd/goal/interact.go
+++ b/cmd/goal/interact.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -29,6 +29,7 @@ import (
"github.com/spf13/cobra"
+ "github.com/algorand/avm-abi/apps"
"github.com/algorand/go-algorand/crypto"
apiclient "github.com/algorand/go-algorand/daemon/algod/api/client"
"github.com/algorand/go-algorand/data/basics"
@@ -513,7 +514,7 @@ var appExecuteCmd = &cobra.Command{
var inputs appCallInputs
for _, arg := range proc.Args {
- var callArg logic.AppCallBytes
+ var callArg apps.AppCallBytes
callArg.Encoding = arg.Kind
if !procFlags.Changed(arg.Name) && arg.Default != "" {
diff --git a/cmd/goal/kmd.go b/cmd/goal/kmd.go
index fa4ef9182..3e9901f5a 100644
--- a/cmd/goal/kmd.go
+++ b/cmd/goal/kmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/ledger.go b/cmd/goal/ledger.go
index d06cf3237..208655ad6 100644
--- a/cmd/goal/ledger.go
+++ b/cmd/goal/ledger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/logging.go b/cmd/goal/logging.go
index a54b46031..eb05c7399 100644
--- a/cmd/goal/logging.go
+++ b/cmd/goal/logging.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/messages.go b/cmd/goal/messages.go
index 3fd1ce36c..8e4dcebf8 100644
--- a/cmd/goal/messages.go
+++ b/cmd/goal/messages.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -55,39 +55,41 @@ const (
errorKMDFailedToStop = "Failed to stop kmd: %s"
// Node
- infoNodeStart = "Algorand node successfully started!"
- infoNodeAlreadyStarted = "Algorand node was already started!"
- infoNodeDidNotRestart = "Algorand node did not restart. The node is still running!"
- infoTryingToStopNode = "Trying to stop the node..."
- infoNodeShuttingDown = "Algorand node is shutting down..."
- infoNodeSuccessfullyStopped = "The node was successfully stopped."
- infoNodeStatus = "Last committed block: %d\nTime since last block: %s\nSync Time: %s\nLast consensus protocol: %s\nNext consensus protocol: %s\nRound for next consensus protocol: %d\nNext consensus protocol supported: %v"
- catchupStoppedOnUnsupported = "Last supported block (%d) is committed. The next block consensus protocol is not supported. Catchup service is stopped."
- infoNodeCatchpointCatchupStatus = "Last committed block: %d\nSync Time: %s\nCatchpoint: %s"
- infoNodeCatchpointCatchupAccounts = "Catchpoint total accounts: %d\nCatchpoint accounts processed: %d\nCatchpoint accounts verified: %d\nCatchpoint total KVs: %d\nCatchpoint KVs processed: %d\nCatchpoint KVs verified: %d"
- infoNodeCatchpointCatchupBlocks = "Catchpoint total blocks: %d\nCatchpoint downloaded blocks: %d"
- nodeLastCatchpoint = "Last Catchpoint: %s"
- errorNodeCreationIPFailure = "Parsing passed IP %v failed: need a valid IPv4 or IPv6 address with a specified port number"
- errorNodeNotDetected = "Algorand node does not appear to be running: %s"
- errorNodeStatus = "Cannot contact Algorand node: %s"
- errorNodeFailedToStart = "Algorand node failed to start: %s"
- errorNodeRunning = "Node must be stopped before writing APIToken"
- errorNodeFailGenToken = "Cannot generate API token: %s"
- errorNodeCreation = "Error during node creation: %v"
- errorNodeManagedBySystemd = "This node is using systemd and should be managed with systemctl. For additional information refer to https://developer.algorand.org/docs/run-a-node/setup/install/#installing-algod-as-a-systemd-service"
- errorKill = "Cannot kill node: %s"
- errorCloningNode = "Error cloning the node: %s"
- infoNodeCloned = "Node cloned successfully to: %s"
- infoNodeWroteToken = "Successfully wrote new API token: %s"
- infoNodePendingTxnsDescription = "Pending Transactions (Truncated max=%d, Total in pool=%d): "
- infoNodeNoPendingTxnsDescription = "None"
- infoDataDir = "[Data Directory: %s]"
- errLoadingConfig = "Error loading Config file from '%s': %v"
- errorNodeFailedToShutdown = "Unable to shut down node: %v"
- errorCatchpointLabelParsingFailed = "The provided catchpoint is not a valid one"
- errorCatchpointLabelMissing = "A catchpoint argument is needed: %s"
- errorUnableToLookupCatchpointLabel = "Unable to fetch catchpoint label"
- errorTooManyCatchpointLabels = "The catchup command expect a single catchpoint"
+ infoNodeStart = "Algorand node successfully started!"
+ infoNodeAlreadyStarted = "Algorand node was already started!"
+ infoNodeDidNotRestart = "Algorand node did not restart. The node is still running!"
+ infoTryingToStopNode = "Trying to stop the node..."
+ infoNodeShuttingDown = "Algorand node is shutting down..."
+ infoNodeSuccessfullyStopped = "The node was successfully stopped."
+ infoNodeStatus = "Last committed block: %d\nTime since last block: %s\nSync Time: %s\nLast consensus protocol: %s\nNext consensus protocol: %s\nRound for next consensus protocol: %d\nNext consensus protocol supported: %v"
+ infoNodeStatusConsensusUpgradeVoting = "Consensus upgrate state: Voting\nYes votes: %d\nNo votes: %d\nVotes remaining: %d\nYes votes required: %d\nVote window close round: %d"
+ infoNodeStatusConsensusUpgradeScheduled = "Consensus upgrade state: Scheduled"
+ catchupStoppedOnUnsupported = "Last supported block (%d) is committed. The next block consensus protocol is not supported. Catchup service is stopped."
+ infoNodeCatchpointCatchupStatus = "Last committed block: %d\nSync Time: %s\nCatchpoint: %s"
+ infoNodeCatchpointCatchupAccounts = "Catchpoint total accounts: %d\nCatchpoint accounts processed: %d\nCatchpoint accounts verified: %d\nCatchpoint total KVs: %d\nCatchpoint KVs processed: %d\nCatchpoint KVs verified: %d"
+ infoNodeCatchpointCatchupBlocks = "Catchpoint total blocks: %d\nCatchpoint downloaded blocks: %d"
+ nodeLastCatchpoint = "Last Catchpoint: %s"
+ errorNodeCreationIPFailure = "Parsing passed IP %v failed: need a valid IPv4 or IPv6 address with a specified port number"
+ errorNodeNotDetected = "Algorand node does not appear to be running: %s"
+ errorNodeStatus = "Cannot contact Algorand node: %s"
+ errorNodeFailedToStart = "Algorand node failed to start: %s"
+ errorNodeRunning = "Node must be stopped before writing APIToken"
+ errorNodeFailGenToken = "Cannot generate API token: %s"
+ errorNodeCreation = "Error during node creation: %v"
+ errorNodeManagedBySystemd = "This node is using systemd and should be managed with systemctl. For additional information refer to https://developer.algorand.org/docs/run-a-node/setup/install/#installing-algod-as-a-systemd-service"
+ errorKill = "Cannot kill node: %s"
+ errorCloningNode = "Error cloning the node: %s"
+ infoNodeCloned = "Node cloned successfully to: %s"
+ infoNodeWroteToken = "Successfully wrote new API token: %s"
+ infoNodePendingTxnsDescription = "Pending Transactions (Truncated max=%d, Total in pool=%d): "
+ infoNodeNoPendingTxnsDescription = "None"
+ infoDataDir = "[Data Directory: %s]"
+ errLoadingConfig = "Error loading Config file from '%s': %v"
+ errorNodeFailedToShutdown = "Unable to shut down node: %v"
+ errorCatchpointLabelParsingFailed = "The provided catchpoint is not a valid one"
+ errorCatchpointLabelMissing = "A catchpoint argument is needed: %s"
+ errorUnableToLookupCatchpointLabel = "Unable to fetch catchpoint label"
+ errorTooManyCatchpointLabels = "The catchup command expect a single catchpoint"
// Asset
malformedMetadataHash = "Cannot base64-decode metadata hash %s: %s"
diff --git a/cmd/goal/multisig.go b/cmd/goal/multisig.go
index 643b335d0..5db3d09af 100644
--- a/cmd/goal/multisig.go
+++ b/cmd/goal/multisig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -163,7 +163,7 @@ var signProgramCmd = &cobra.Command{
}
ops, err := logic.AssembleString(string(text))
if err != nil {
- ops.ReportProblems(programSource, os.Stderr)
+ ops.ReportMultipleErrors(programSource, os.Stderr)
reportErrorf("%s: %s", programSource, err)
}
if outname == "" {
diff --git a/cmd/goal/network.go b/cmd/goal/network.go
index ff4b7005b..35565fb6f 100644
--- a/cmd/goal/network.go
+++ b/cmd/goal/network.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/node.go b/cmd/goal/node.go
index a17551c47..fcfa27d6d 100644
--- a/cmd/goal/node.go
+++ b/cmd/goal/node.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -440,6 +440,7 @@ func makeStatusString(stat model.NodeStatusResponse) string {
lastRoundTime := fmt.Sprintf("%.1fs", time.Duration(stat.TimeSinceLastRound).Seconds())
catchupTime := fmt.Sprintf("%.1fs", time.Duration(stat.CatchupTime).Seconds())
var statusString string
+
if stat.Catchpoint == nil || (*stat.Catchpoint) == "" {
statusString = fmt.Sprintf(
infoNodeStatus,
@@ -458,6 +459,37 @@ func makeStatusString(stat model.NodeStatusResponse) string {
if stat.StoppedAtUnsupportedRound {
statusString = statusString + "\n" + fmt.Sprintf(catchupStoppedOnUnsupported, stat.LastRound)
}
+
+ upgradeNextProtocolVoteBefore := uint64(0)
+ if stat.UpgradeNextProtocolVoteBefore != nil {
+ upgradeNextProtocolVoteBefore = *stat.UpgradeNextProtocolVoteBefore
+ }
+
+ if upgradeNextProtocolVoteBefore > stat.LastRound {
+ upgradeVotesRequired := uint64(0)
+ upgradeNoVotes := uint64(0)
+ upgradeYesVotes := uint64(0)
+ if stat.UpgradeVotesRequired != nil {
+ upgradeVotesRequired = *stat.UpgradeVotesRequired
+ }
+ if stat.UpgradeNoVotes != nil {
+ upgradeNoVotes = *stat.UpgradeNoVotes
+ }
+ if stat.UpgradeYesVotes != nil {
+ upgradeYesVotes = *stat.UpgradeYesVotes
+ }
+ statusString = statusString + "\n" + fmt.Sprintf(
+ infoNodeStatusConsensusUpgradeVoting,
+ upgradeYesVotes,
+ upgradeNoVotes,
+ upgradeNextProtocolVoteBefore-stat.LastRound,
+ upgradeVotesRequired,
+ upgradeNextProtocolVoteBefore,
+ )
+ } else if upgradeNextProtocolVoteBefore > 0 {
+ statusString = statusString + "\n" + infoNodeStatusConsensusUpgradeScheduled
+ }
+
} else {
statusString = fmt.Sprintf(
infoNodeCatchpointCatchupStatus,
diff --git a/cmd/goal/node_test.go b/cmd/goal/node_test.go
index 3c35485bf..aa3829cfb 100644
--- a/cmd/goal/node_test.go
+++ b/cmd/goal/node_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/tealsign.go b/cmd/goal/tealsign.go
index 5ff7617d0..33af6ca01 100644
--- a/cmd/goal/tealsign.go
+++ b/cmd/goal/tealsign.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/goal/wallet.go b/cmd/goal/wallet.go
index 90faa86c0..800d4b179 100644
--- a/cmd/goal/wallet.go
+++ b/cmd/goal/wallet.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/incorporate/incorporate.go b/cmd/incorporate/incorporate.go
index 48a2ac830..38631a101 100644
--- a/cmd/incorporate/incorporate.go
+++ b/cmd/incorporate/incorporate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/kmd/codes/codes.go b/cmd/kmd/codes/codes.go
index 12b0fb38f..ecfe57352 100644
--- a/cmd/kmd/codes/codes.go
+++ b/cmd/kmd/codes/codes.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/kmd/main.go b/cmd/kmd/main.go
index 6d15aef48..92bfe62ac 100644
--- a/cmd/kmd/main.go
+++ b/cmd/kmd/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/kmd/mlock_darwin.go b/cmd/kmd/mlock_darwin.go
index 14791e7fe..5e6e75ccc 100644
--- a/cmd/kmd/mlock_darwin.go
+++ b/cmd/kmd/mlock_darwin.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/kmd/mlock_linux.go b/cmd/kmd/mlock_linux.go
index ff295866a..c650ec15c 100644
--- a/cmd/kmd/mlock_linux.go
+++ b/cmd/kmd/mlock_linux.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/kmd/mlock_windows.go b/cmd/kmd/mlock_windows.go
index 64e82811f..20db381e2 100644
--- a/cmd/kmd/mlock_windows.go
+++ b/cmd/kmd/mlock_windows.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/loadgenerator/config.go b/cmd/loadgenerator/config.go
index 8a3a13cdb..5ffb86fd7 100644
--- a/cmd/loadgenerator/config.go
+++ b/cmd/loadgenerator/config.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/loadgenerator/main.go b/cmd/loadgenerator/main.go
index 54f1f5248..6ef8cd238 100644
--- a/cmd/loadgenerator/main.go
+++ b/cmd/loadgenerator/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/msgpacktool/main.go b/cmd/msgpacktool/main.go
index fac70fcec..b6e359a0d 100644
--- a/cmd/msgpacktool/main.go
+++ b/cmd/msgpacktool/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/netdummy/main.go b/cmd/netdummy/main.go
index 92d96adcd..cdc85e416 100644
--- a/cmd/netdummy/main.go
+++ b/cmd/netdummy/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/netgoal/commands.go b/cmd/netgoal/commands.go
index 30522e87d..6fba6f8fa 100644
--- a/cmd/netgoal/commands.go
+++ b/cmd/netgoal/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/netgoal/generate.go b/cmd/netgoal/generate.go
index 0eed602bf..88653df88 100644
--- a/cmd/netgoal/generate.go
+++ b/cmd/netgoal/generate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/netgoal/messages.go b/cmd/netgoal/messages.go
index 1db5a0828..a575c4735 100644
--- a/cmd/netgoal/messages.go
+++ b/cmd/netgoal/messages.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/netgoal/network.go b/cmd/netgoal/network.go
index 4ebba13f1..f0efdc2cd 100644
--- a/cmd/netgoal/network.go
+++ b/cmd/netgoal/network.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/netgoal/recipe.go b/cmd/netgoal/recipe.go
index ba8af9b76..c1356b73f 100644
--- a/cmd/netgoal/recipe.go
+++ b/cmd/netgoal/recipe.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/nodecfg/apply.go b/cmd/nodecfg/apply.go
index 77e302c7b..689476537 100644
--- a/cmd/nodecfg/apply.go
+++ b/cmd/nodecfg/apply.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -34,6 +34,7 @@ var applyRootDir string
var applyRootNodeDir string
var applyPublicAddress string
var nodeConfigBucket string
+var disableDNS bool
func init() {
applyCmd.Flags().StringVarP(&applyChannel, "channel", "c", "", "Channel for the nodes we are configuring")
@@ -49,6 +50,8 @@ func init() {
applyCmd.Flags().StringVarP(&applyPublicAddress, "publicaddress", "a", "", "The public address to use if registering Relay or for Metrics")
applyCmd.Flags().StringVarP(&nodeConfigBucket, "bucket", "b", "", "S3 bucket to get node configuration from.")
+
+ applyCmd.Flags().BoolVarP(&disableDNS, "disable-dns", "N", false, "disable setting DNS entries")
}
var applyCmd = &cobra.Command{
@@ -122,7 +125,7 @@ func doApply(rootDir string, rootNodeDir, channel string, hostName string, dnsNa
return fmt.Errorf("configuration does not include this host: %s", hostName)
}
- if hostNeedsDNSName(hostCfg) && dnsName == "" {
+ if hostNeedsDNSName(hostCfg) && dnsName == "" && !disableDNS {
return fmt.Errorf("publicaddress is required - Host contains Relays or exposes Metrics")
}
diff --git a/cmd/nodecfg/commands.go b/cmd/nodecfg/commands.go
index af3dc08a8..ef6b90818 100644
--- a/cmd/nodecfg/commands.go
+++ b/cmd/nodecfg/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/nodecfg/download.go b/cmd/nodecfg/download.go
index 762c0ae86..fd59e0dff 100644
--- a/cmd/nodecfg/download.go
+++ b/cmd/nodecfg/download.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/nodecfg/get.go b/cmd/nodecfg/get.go
index 707509f66..6ca11c736 100644
--- a/cmd/nodecfg/get.go
+++ b/cmd/nodecfg/get.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/opdoc/opdoc.go b/cmd/opdoc/opdoc.go
index 1a1e9941d..e0b69fa11 100644
--- a/cmd/opdoc/opdoc.go
+++ b/cmd/opdoc/opdoc.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -270,14 +270,20 @@ func typeString(types []logic.StackType) string {
case logic.StackAny:
out[i] = '.'
case logic.StackNone:
- if i == 0 && len(types) == 1 {
- return ""
- }
- panic("unexpected StackNone in opdoc typeString")
+ out[i] = '_'
default:
panic("unexpected type in opdoc typeString")
}
}
+
+ // Cant return None and !None from same op
+ if strings.Contains(string(out), "_") {
+ if strings.ContainsAny(string(out), "UB.") {
+ panic("unexpected StackNone in opdoc typeString")
+ }
+ return ""
+ }
+
return string(out)
}
@@ -295,6 +301,9 @@ func fieldsAndTypes(group logic.FieldGroup) ([]string, string) {
}
func argEnums(name string) ([]string, string) {
+ // reminder: this needs to be manually updated every time
+ // a new opcode is added with an associated FieldGroup
+ // it'd be nice to have this auto-update
switch name {
case "txn", "gtxn", "gtxns", "itxn", "gitxn":
return fieldsAndTypes(logic.TxnFields)
@@ -314,6 +323,16 @@ func argEnums(name string) ([]string, string) {
return fieldsAndTypes(logic.AppParamsFields)
case "acct_params_get":
return fieldsAndTypes(logic.AcctParamsFields)
+ case "block":
+ return fieldsAndTypes(logic.BlockFields)
+ case "json_ref":
+ return fieldsAndTypes(logic.JSONRefTypes)
+ case "base64_decode":
+ return fieldsAndTypes(logic.Base64Encodings)
+ case "vrf_verify":
+ return fieldsAndTypes(logic.VrfStandards)
+ case "ecdsa_pk_recover", "ecdsa_verify", "ecdsa_pk_decompress":
+ return fieldsAndTypes(logic.EcdsaCurves)
default:
return nil, ""
}
diff --git a/cmd/opdoc/tmLanguage.go b/cmd/opdoc/tmLanguage.go
index 6501babc0..a24c3dfd8 100644
--- a/cmd/opdoc/tmLanguage.go
+++ b/cmd/opdoc/tmLanguage.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/partitiontest_linter/go.mod b/cmd/partitiontest_linter/go.mod
index 3e9db30e9..57f9fbb1f 100644
--- a/cmd/partitiontest_linter/go.mod
+++ b/cmd/partitiontest_linter/go.mod
@@ -2,7 +2,8 @@ module github.com/algorand/go-algorand/cmd/partitiontest_linter
go 1.17
-require golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
-require golang.org/x/sys v0.0.0-20210510120138-977fb7262007
-require golang.org/x/mod v0.4.2
-require golang.org/x/tools v0.1.3
+require golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
+
+require golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+
+require golang.org/x/tools v0.1.12
diff --git a/cmd/partitiontest_linter/go.sum b/cmd/partitiontest_linter/go.sum
index e652f3edc..4965383ee 100644
--- a/cmd/partitiontest_linter/go.sum
+++ b/cmd/partitiontest_linter/go.sum
@@ -1,27 +1,6 @@
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
diff --git a/cmd/partitiontest_linter/linter.go b/cmd/partitiontest_linter/linter.go
index 8d5ab728d..50c1298a0 100644
--- a/cmd/partitiontest_linter/linter.go
+++ b/cmd/partitiontest_linter/linter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -124,7 +124,7 @@ func doesParameterNameMatch(call *ast.CallExpr, fn *ast.FuncDecl) bool {
for _, oneArg := range call.Args {
if realArg, ok := oneArg.(*ast.Ident); ok {
- if realArg.Obj.Name == fn.Type.Params.List[0].Names[0].Obj.Name {
+ if realArg != nil && realArg.Obj != nil && realArg.Obj.Name == fn.Type.Params.List[0].Names[0].Obj.Name {
return true
}
}
diff --git a/cmd/partitiontest_linter/linter_test.go b/cmd/partitiontest_linter/linter_test.go
index d62fcef73..26a3e85be 100644
--- a/cmd/partitiontest_linter/linter_test.go
+++ b/cmd/partitiontest_linter/linter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/partitiontest_linter/plugin/plugin.go b/cmd/partitiontest_linter/plugin/plugin.go
index 587529fea..ee229371f 100644
--- a/cmd/partitiontest_linter/plugin/plugin.go
+++ b/cmd/partitiontest_linter/plugin/plugin.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/partitiontest_linter/testdata/linter_testdata_test.go b/cmd/partitiontest_linter/testdata/linter_testdata_test.go
index 3658e6211..d84cb837c 100644
--- a/cmd/partitiontest_linter/testdata/linter_testdata_test.go
+++ b/cmd/partitiontest_linter/testdata/linter_testdata_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/pingpong/commands.go b/cmd/pingpong/commands.go
index 71733f22f..7e78eedb0 100644
--- a/cmd/pingpong/commands.go
+++ b/cmd/pingpong/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/pingpong/runCmd.go b/cmd/pingpong/runCmd.go
index 4e516423c..7813d4f31 100644
--- a/cmd/pingpong/runCmd.go
+++ b/cmd/pingpong/runCmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -90,6 +90,7 @@ var generatedAccountsCount uint64
var generatedAccountsOffset uint64
var generatedAccountSampleMethod string
var configPath string
+var latencyPath string
func init() {
rootCmd.AddCommand(runCmd)
@@ -111,6 +112,7 @@ func init() {
runCmd.Flags().StringVar(&refreshTime, "refresh", "", "Duration of time (seconds) between refilling accounts with money (0 means no refresh)")
runCmd.Flags().StringVar(&logicProg, "program", "", "File containing the compiled program to include as a logic sig")
runCmd.Flags().StringVar(&configPath, "config", "", "path to read config json from, or json literal")
+ runCmd.Flags().StringVar(&latencyPath, "latency", "", "path to write txn latency log to (.gz for compressed)")
runCmd.Flags().BoolVar(&saveConfig, "save", false, "Save the effective configuration to disk")
runCmd.Flags().BoolVar(&useDefault, "reset", false, "Reset to the default configuration (not read from disk)")
runCmd.Flags().BoolVar(&quietish, "quiet", false, "quietish stdout logging")
@@ -316,7 +318,7 @@ var runCmd = &cobra.Command{
}
ops, err := logic.AssembleString(programStr)
if err != nil {
- ops.ReportProblems(teal, os.Stderr)
+ ops.ReportMultipleErrors(teal, os.Stderr)
reportErrorf("Internal error, cannot assemble %v \n", programStr)
}
cfg.Program = ops.Program
@@ -440,6 +442,10 @@ var runCmd = &cobra.Command{
reportErrorf("numAccounts is greater than number of account mnemonics provided")
}
+ if latencyPath != "" {
+ cfg.TotalLatencyOut = latencyPath
+ }
+
cfg.SetDefaultWeights()
err = cfg.Check()
if err != nil {
diff --git a/cmd/pingpong/teal_programs.go b/cmd/pingpong/teal_programs.go
index e56584211..150877188 100644
--- a/cmd/pingpong/teal_programs.go
+++ b/cmd/pingpong/teal_programs.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/README.md b/cmd/tealdbg/README.md
index b5a7bebbf..14967677f 100644
--- a/cmd/tealdbg/README.md
+++ b/cmd/tealdbg/README.md
@@ -9,6 +9,7 @@
- [Protocol](#protocol)
- [Transaction and Transaction Group](#transaction-and-transaction-group)
- [Balance records](#balance-records)
+ - [Indexer Support](#indexer-support)
- [Execution mode](#execution-mode)
- [Chrome DevTools Frontend Features](#chrome-devtools-frontend-features)
- [Configure the Listener](#configure-the-listener)
@@ -44,7 +45,7 @@ and balance records (see [Setting Debug Context](#setting-debug-context) for det
Remote debugger might be useful for debugging unit tests for TEAL (currently in Golang only) or for hacking **algod** `eval` and breaking on any TEAL evaluation.
The protocol consist of three REST endpoints and one data structure describing the evaluator state.
-See `WebDebuggerHook` and `TestWebDebuggerManual` in [go-algorand sources](https://github.com/algorand/go-algorand/tree/master/data/transactions/logic) for more details.
+See `WebDebugger` and `TestWebDebuggerManual` in [go-algorand sources](https://github.com/algorand/go-algorand/tree/master/data/transactions/logic) for more details.
### Frontends
@@ -206,13 +207,18 @@ Refer to the [Chrome DevTools debugging](https://developers.google.com/web/tools
The evaluator accepts a new `Debugger` parameter described as the interface:
```golang
-type DebuggerHook interface {
+// Debugger is an interface that supports the first version of AVM debuggers.
+// It consists of a set of functions called by eval function during AVM program execution.
+//
+// Deprecated: This interface does not support non-app call or inner transactions. Use EvalTracer
+// instead.
+type Debugger interface {
// Register is fired on program creation
- Register(state *DebugState) error
+ Register(state *DebugState)
// Update is fired on every step
- Update(state *DebugState) error
+ Update(state *DebugState)
// Complete is called when the program exits
- Complete(state *DebugState) error
+ Complete(state *DebugState)
}
```
If `Debugger` is set the evaluator calls `Register` on creation, `Update` on every step and `Complete` on exit.
@@ -251,13 +257,13 @@ The core calls `SessionEnded` on `Complete` call.
If one needs to debug TEAL in as much real environment as possible then do
-1. Add `WebDebuggerHook` to `data/transactions/logic/eval.go`:
+1. Add `WebDebugger` to `data/transactions/logic/eval.go`:
```golang
cx.program = program
// begin new code
debugURL := os.Getenv("TEAL_DEBUGGER_URL")
- cx.Debugger = &WebDebuggerHook{URL: debugURL}
+ cx.Debugger = &WebDebugger{URL: debugURL}
// end new code
if cx.Debugger != nil {
diff --git a/cmd/tealdbg/bundle_home_html.sh b/cmd/tealdbg/bundle_home_html.sh
index 095095420..8c83a4aaa 100755
--- a/cmd/tealdbg/bundle_home_html.sh
+++ b/cmd/tealdbg/bundle_home_html.sh
@@ -3,7 +3,7 @@
THISDIR=$(dirname $0)
cat <<EOM | gofmt > $THISDIR/homepage.go
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/cdt/proto.go b/cmd/tealdbg/cdt/proto.go
index ad527fc09..dd28fcd82 100644
--- a/cmd/tealdbg/cdt/proto.go
+++ b/cmd/tealdbg/cdt/proto.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/cdtSession.go b/cmd/tealdbg/cdtSession.go
index 3ae5675b3..f7b74eb9e 100644
--- a/cmd/tealdbg/cdtSession.go
+++ b/cmd/tealdbg/cdtSession.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/cdtSession_test.go b/cmd/tealdbg/cdtSession_test.go
index 1668c1a03..89a2ce1b0 100644
--- a/cmd/tealdbg/cdtSession_test.go
+++ b/cmd/tealdbg/cdtSession_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/cdtState.go b/cmd/tealdbg/cdtState.go
index 21273982f..2ee335a21 100644
--- a/cmd/tealdbg/cdtState.go
+++ b/cmd/tealdbg/cdtState.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/cdtStateObjects.go b/cmd/tealdbg/cdtStateObjects.go
index b6daf39f1..377492d23 100644
--- a/cmd/tealdbg/cdtStateObjects.go
+++ b/cmd/tealdbg/cdtStateObjects.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/cdtdbg.go b/cmd/tealdbg/cdtdbg.go
index fd88bf92e..45de17d3e 100644
--- a/cmd/tealdbg/cdtdbg.go
+++ b/cmd/tealdbg/cdtdbg.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/cdtdbg_test.go b/cmd/tealdbg/cdtdbg_test.go
index 41164a3af..a4e817735 100644
--- a/cmd/tealdbg/cdtdbg_test.go
+++ b/cmd/tealdbg/cdtdbg_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/debugger.go b/cmd/tealdbg/debugger.go
index b9b899560..3639a41af 100644
--- a/cmd/tealdbg/debugger.go
+++ b/cmd/tealdbg/debugger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -26,6 +26,7 @@ import (
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions/logic"
+ "github.com/algorand/go-algorand/logging"
)
// Notification is sent to the client over their websocket connection
@@ -528,7 +529,7 @@ func (d *Debugger) SaveProgram(
}
// Register setups new session and notifies frontends if any
-func (d *Debugger) Register(state *logic.DebugState) error {
+func (d *Debugger) Register(state *logic.DebugState) {
sid := state.ExecID
pcOffset := make(map[int]int, len(state.PCOffset))
for _, pco := range state.PCOffset {
@@ -556,12 +557,17 @@ func (d *Debugger) Register(state *logic.DebugState) error {
// Wait for acknowledgement
<-s.acknowledged
-
- return nil
}
// Update process state update notifications: pauses or continues as needed
-func (d *Debugger) Update(state *logic.DebugState) error {
+func (d *Debugger) Update(state *logic.DebugState) {
+ err := d.update(state)
+ if err != nil {
+ logging.Base().Errorf("error in Update hook: %s", err.Error())
+ }
+}
+
+func (d *Debugger) update(state *logic.DebugState) error {
sid := state.ExecID
s, err := d.getSession(sid)
if err != nil {
@@ -596,7 +602,14 @@ func (d *Debugger) Update(state *logic.DebugState) error {
}
// Complete terminates session and notifies frontends if any
-func (d *Debugger) Complete(state *logic.DebugState) error {
+func (d *Debugger) Complete(state *logic.DebugState) {
+ err := d.complete(state)
+ if err != nil {
+ logging.Base().Errorf("error in Complete hook: %s", err.Error())
+ }
+}
+
+func (d *Debugger) complete(state *logic.DebugState) error {
sid := state.ExecID
s, err := d.getSession(sid)
if err != nil {
diff --git a/cmd/tealdbg/debugger_test.go b/cmd/tealdbg/debugger_test.go
index 4f4b35ea2..a73fb3f4f 100644
--- a/cmd/tealdbg/debugger_test.go
+++ b/cmd/tealdbg/debugger_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -102,7 +102,7 @@ func TestDebuggerSimple(t *testing.T) {
debugger.AddAdapter(da)
ep := logic.NewEvalParams(make([]transactions.SignedTxnWithAD, 1), &proto, nil)
- ep.Debugger = debugger
+ ep.Tracer = logic.MakeEvalTracerDebuggerAdaptor(debugger)
ep.SigLedger = logic.NoHeaderLedger{}
source := `int 0
diff --git a/cmd/tealdbg/dryrunRequest.go b/cmd/tealdbg/dryrunRequest.go
index b6973cec9..5f13aa6cf 100644
--- a/cmd/tealdbg/dryrunRequest.go
+++ b/cmd/tealdbg/dryrunRequest.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -47,23 +47,6 @@ func ddrFromParams(dp *DebugParams) (ddr v2.DryrunRequest, err error) {
return
}
-func convertAccounts(accounts []model.Account) (records []basics.BalanceRecord, err error) {
- for _, a := range accounts {
- var addr basics.Address
- addr, err = basics.UnmarshalChecksumAddress(a.Address)
- if err != nil {
- return
- }
- var ad basics.AccountData
- ad, err = v2.AccountToAccountData(&a)
- if err != nil {
- return
- }
- records = append(records, basics.BalanceRecord{Addr: addr, AccountData: ad})
- }
- return
-}
-
func balanceRecordsFromDdr(ddr *v2.DryrunRequest) (records []basics.BalanceRecord, err error) {
accounts := make(map[basics.Address]basics.AccountData)
for _, a := range ddr.Accounts {
diff --git a/cmd/tealdbg/homepage.go b/cmd/tealdbg/homepage.go
index 4af43c4c8..7e77ed291 100644
--- a/cmd/tealdbg/homepage.go
+++ b/cmd/tealdbg/homepage.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/local.go b/cmd/tealdbg/local.go
index fa3c5d6fc..8e9d9b81f 100644
--- a/cmd/tealdbg/local.go
+++ b/cmd/tealdbg/local.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -536,7 +536,7 @@ func (r *LocalRunner) RunAll() error {
// ep.Debugger = r.debugger
// if ep.Debugger != nil // FALSE
if r.debugger != nil {
- ep.Debugger = r.debugger
+ ep.Tracer = logic.MakeEvalTracerDebuggerAdaptor(r.debugger)
}
}
diff --git a/cmd/tealdbg/localLedger.go b/cmd/tealdbg/localLedger.go
index b045466df..c76039c7b 100644
--- a/cmd/tealdbg/localLedger.go
+++ b/cmd/tealdbg/localLedger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/localLedger_test.go b/cmd/tealdbg/localLedger_test.go
index 3dad5508c..cf53b53d8 100644
--- a/cmd/tealdbg/localLedger_test.go
+++ b/cmd/tealdbg/localLedger_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/local_test.go b/cmd/tealdbg/local_test.go
index f39c9da8a..22fee98f6 100644
--- a/cmd/tealdbg/local_test.go
+++ b/cmd/tealdbg/local_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/main.go b/cmd/tealdbg/main.go
index 75e437c14..461ed0d03 100644
--- a/cmd/tealdbg/main.go
+++ b/cmd/tealdbg/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/remote.go b/cmd/tealdbg/remote.go
index 05b468979..0656e418d 100644
--- a/cmd/tealdbg/remote.go
+++ b/cmd/tealdbg/remote.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -26,7 +26,7 @@ import (
"github.com/algorand/go-algorand/protocol"
)
-// RemoteHookAdapter provides HTTP transport for WebDebuggerHook
+// RemoteHookAdapter provides HTTP transport for WebDebugger
type RemoteHookAdapter struct {
debugger *Debugger
}
@@ -38,7 +38,7 @@ func MakeRemoteHook(debugger *Debugger) *RemoteHookAdapter {
return r
}
-// Setup adds HTTP handlers for remote WebDebuggerHook
+// Setup adds HTTP handlers for remote WebDebugger
func (rha *RemoteHookAdapter) Setup(router *mux.Router) {
router.HandleFunc("/exec/register", rha.registerHandler).Methods("POST")
router.HandleFunc("/exec/update", rha.updateHandler).Methods("POST")
@@ -59,11 +59,7 @@ func (rha *RemoteHookAdapter) registerHandler(w http.ResponseWriter, r *http.Req
}
// Register, and wait for user to acknowledge registration
- err = rha.debugger.Register(&state)
- if err != nil {
- w.WriteHeader(http.StatusBadRequest)
- return
- }
+ rha.debugger.Register(&state)
// Proceed!
w.WriteHeader(http.StatusOK)
@@ -78,7 +74,7 @@ func (rha *RemoteHookAdapter) updateHandler(w http.ResponseWriter, r *http.Reque
}
// Ask debugger to process and wait to continue
- err = rha.debugger.Update(&state)
+ err = rha.debugger.update(&state)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
@@ -96,7 +92,7 @@ func (rha *RemoteHookAdapter) completeHandler(w http.ResponseWriter, r *http.Req
}
// Ask debugger to process and wait to continue
- err = rha.debugger.Complete(&state)
+ err = rha.debugger.complete(&state)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
diff --git a/cmd/tealdbg/remote_test.go b/cmd/tealdbg/remote_test.go
index 5ac654cd7..66d077981 100644
--- a/cmd/tealdbg/remote_test.go
+++ b/cmd/tealdbg/remote_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/server.go b/cmd/tealdbg/server.go
index cf062af62..695cb030d 100644
--- a/cmd/tealdbg/server.go
+++ b/cmd/tealdbg/server.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/server_test.go b/cmd/tealdbg/server_test.go
index a91be7181..81c6c2837 100644
--- a/cmd/tealdbg/server_test.go
+++ b/cmd/tealdbg/server_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/util.go b/cmd/tealdbg/util.go
index d611c7bcb..d91220e71 100644
--- a/cmd/tealdbg/util.go
+++ b/cmd/tealdbg/util.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/webdbg.go b/cmd/tealdbg/webdbg.go
index fe6058d9b..d237d70af 100644
--- a/cmd/tealdbg/webdbg.go
+++ b/cmd/tealdbg/webdbg.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/tealdbg/webdbg_test.go b/cmd/tealdbg/webdbg_test.go
index be6801d63..d45ac9f21 100644
--- a/cmd/tealdbg/webdbg_test.go
+++ b/cmd/tealdbg/webdbg_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/updater/commands.go b/cmd/updater/commands.go
index 03d2f2a48..cc152ff5d 100644
--- a/cmd/updater/commands.go
+++ b/cmd/updater/commands.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/updater/sendCmd.go b/cmd/updater/sendCmd.go
index d3a4a0cd9..d194d8ca9 100644
--- a/cmd/updater/sendCmd.go
+++ b/cmd/updater/sendCmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/updater/toolsCmd.go b/cmd/updater/toolsCmd.go
index d23644252..4ebaa8b4a 100644
--- a/cmd/updater/toolsCmd.go
+++ b/cmd/updater/toolsCmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/updater/update.sh b/cmd/updater/update.sh
index cc4403ffd..94a4b2ba2 100755
--- a/cmd/updater/update.sh
+++ b/cmd/updater/update.sh
@@ -334,7 +334,7 @@ function check_for_updater() {
function check_for_update() {
determine_current_version
check_for_updater
- LATEST="$(${SCRIPTPATH}/updater ver check -c ${CHANNEL} ${BUCKET} | sed -n '2 p')"
+ LATEST="$(${SCRIPTPATH}/updater ver check -c ${CHANNEL} ${BUCKET} | tail -1)"
if [ $? -ne 0 ]; then
echo "No remote updates found"
return 1
diff --git a/cmd/updater/util.go b/cmd/updater/util.go
index 0bb03baa0..14fa62cf5 100644
--- a/cmd/updater/util.go
+++ b/cmd/updater/util.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/updater/versionCmd.go b/cmd/updater/versionCmd.go
index aeb24a7a9..35713f9b3 100644
--- a/cmd/updater/versionCmd.go
+++ b/cmd/updater/versionCmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/updater/version_test.go b/cmd/updater/version_test.go
index 34f23499e..a29658d53 100644
--- a/cmd/updater/version_test.go
+++ b/cmd/updater/version_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/cmd/util/cmd.go b/cmd/util/cmd.go
index e38972e9c..3b8b1c884 100644
--- a/cmd/util/cmd.go
+++ b/cmd/util/cmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/components/mocks/mockCatchpointCatchupAccessor.go b/components/mocks/mockCatchpointCatchupAccessor.go
index 00758de72..0b45f42be 100644
--- a/components/mocks/mockCatchpointCatchupAccessor.go
+++ b/components/mocks/mockCatchpointCatchupAccessor.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/components/mocks/mockNetwork.go b/components/mocks/mockNetwork.go
index 77e5f5878..8c8eb113f 100644
--- a/components/mocks/mockNetwork.go
+++ b/components/mocks/mockNetwork.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/components/mocks/mockParticipationRegistry.go b/components/mocks/mockParticipationRegistry.go
index d7a53c36f..6af12ef1d 100644
--- a/components/mocks/mockParticipationRegistry.go
+++ b/components/mocks/mockParticipationRegistry.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/config/buildvars.go b/config/buildvars.go
index a9a67d584..af0bfe523 100644
--- a/config/buildvars.go
+++ b/config/buildvars.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/config/config.go b/config/config.go
index c1c9893ca..a180a9a47 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -77,6 +77,10 @@ const ParticipationRegistryFilename = "partregistry.sqlite"
// built-in supported consensus protocols.
const ConfigurableConsensusProtocolsFilename = "consensus.json"
+// The default gossip fanout setting when configured as a relay (here, as we
+// do not expose in normal config so it is not in code generated local_defaults.go
+const defaultRelayGossipFanout = 8
+
// LoadConfigFromDisk returns a Local config structure based on merging the defaults
// with settings loaded from the config file from the custom dir. If the custom file
// cannot be loaded, the default config is returned (with the error from loading the
@@ -124,6 +128,12 @@ func mergeConfigFromFile(configpath string, source Local) (Local, error) {
source.Archival = true
source.EnableLedgerService = true
source.EnableBlockService = true
+
+ // If gossip fanout has not been explicitly overridden, use defaultRelayGossipFanout
+ // rather then the default gossip fanout setting from defaultLocal
+ if source.GossipFanout == defaultLocal.GossipFanout {
+ source.GossipFanout = defaultRelayGossipFanout
+ }
}
return source, err
diff --git a/config/config_test.go b/config/config_test.go
index db35269ad..1e24fa459 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -42,8 +42,9 @@ var defaultConfig = Local{
BaseLoggerDebugLevel: 1, //Info level
}
-func TestSaveThenLoad(t *testing.T) {
+func TestLocal_SaveThenLoad(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
c1, err := loadWithoutDefaults(defaultConfig)
require.NoError(t, err)
@@ -53,13 +54,10 @@ func TestSaveThenLoad(t *testing.T) {
ser1 := json.NewEncoder(&b1)
ser1.Encode(c1)
- os.RemoveAll("testdir")
- err = os.Mkdir("testdir", 0777)
- require.NoError(t, err)
-
- c1.SaveToDisk("testdir")
+ tempDir := t.TempDir()
+ c1.SaveToDisk(tempDir)
- c2, err := LoadConfigFromDisk("testdir")
+ c2, err := LoadConfigFromDisk(tempDir)
require.NoError(t, err)
var b2 bytes.Buffer
@@ -67,23 +65,23 @@ func TestSaveThenLoad(t *testing.T) {
ser2.Encode(c2)
require.True(t, bytes.Equal(b1.Bytes(), b2.Bytes()))
-
- os.RemoveAll("testdir")
}
-func TestLoadMissing(t *testing.T) {
+func TestConfig_LoadMissing(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
- os.RemoveAll("testdir")
- _, err := LoadConfigFromDisk("testdir")
+ tempDir := t.TempDir()
+ os.RemoveAll(tempDir)
+ _, err := LoadConfigFromDisk(tempDir)
require.True(t, os.IsNotExist(err))
}
-func TestMergeConfig(t *testing.T) {
+func TestLocal_MergeConfig(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
- os.RemoveAll("testdir")
- err := os.Mkdir("testdir", 0777)
+ tempDir := t.TempDir()
c1 := struct {
GossipFanout int
@@ -98,7 +96,7 @@ func TestMergeConfig(t *testing.T) {
c1.NetAddress = testString
// write our reduced version of the Local struct
- fileToMerge := filepath.Join("testdir", ConfigFilename)
+ fileToMerge := filepath.Join(tempDir, ConfigFilename)
f, err := os.OpenFile(fileToMerge, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err == nil {
enc := json.NewEncoder(f)
@@ -110,7 +108,7 @@ func TestMergeConfig(t *testing.T) {
// Take defaultConfig and merge with the saved custom settings.
// This should result in c2 being the same as defaultConfig except for the value(s) in our custom c1
- c2, err := mergeConfigFromDir("testdir", defaultConfig)
+ c2, err := mergeConfigFromDir(tempDir, defaultConfig)
require.NoError(t, err)
require.Equal(t, defaultConfig.Archival || c1.NetAddress != "", c2.Archival)
@@ -119,12 +117,10 @@ func TestMergeConfig(t *testing.T) {
require.Equal(t, c1.NetAddress, c2.NetAddress)
require.Equal(t, c1.GossipFanout, c2.GossipFanout)
-
- os.RemoveAll("testdir")
}
-func saveFullPhonebook(phonebook phonebookBlackWhiteList) error {
- filename := filepath.Join("testdir", PhonebookFilename)
+func saveFullPhonebook(phonebook phonebookBlackWhiteList, saveToDir string) error {
+ filename := filepath.Join(saveToDir, PhonebookFilename)
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err == nil {
defer f.Close()
@@ -142,52 +138,48 @@ var phonebookToMerge = phonebookBlackWhiteList{
Include: []string{"test1", "addThisOne"},
}
-var expectedMerged = []string{
- "test1", "test2", "addThisOne",
-}
-
func TestLoadPhonebook(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
- os.RemoveAll("testdir")
- err := os.Mkdir("testdir", 0777)
- require.NoError(t, err)
+ tempDir := t.TempDir()
- err = saveFullPhonebook(phonebook)
+ err := saveFullPhonebook(phonebook, tempDir)
require.NoError(t, err)
- phonebookEntries, err := LoadPhonebook("testdir")
+ phonebookEntries, err := LoadPhonebook(tempDir)
require.NoError(t, err)
require.Equal(t, 3, len(phonebookEntries))
for index, entry := range phonebookEntries {
require.Equal(t, phonebook.Include[index], entry)
}
- os.RemoveAll("testdir")
}
func TestLoadPhonebookMissing(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
- os.RemoveAll("testdir")
- _, err := LoadPhonebook("testdir")
+ tempDir := t.TempDir()
+ _, err := LoadPhonebook(tempDir)
require.True(t, os.IsNotExist(err))
}
func TestArchivalIfRelay(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
testArchivalIfRelay(t, true)
}
func TestArchivalIfNotRelay(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
testArchivalIfRelay(t, false)
}
func testArchivalIfRelay(t *testing.T, relay bool) {
- os.RemoveAll("testdir")
- err := os.Mkdir("testdir", 0777)
+ tempDir := t.TempDir()
c1 := struct {
NetAddress string
@@ -197,7 +189,7 @@ func testArchivalIfRelay(t *testing.T, relay bool) {
}
// write our reduced version of the Local struct
- fileToMerge := filepath.Join("testdir", ConfigFilename)
+ fileToMerge := filepath.Join(tempDir, ConfigFilename)
f, err := os.OpenFile(fileToMerge, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err == nil {
enc := json.NewEncoder(f)
@@ -207,18 +199,16 @@ func testArchivalIfRelay(t *testing.T, relay bool) {
require.NoError(t, err)
require.False(t, defaultConfig.Archival, "Default should be non-archival")
- c2, err := mergeConfigFromDir("testdir", defaultConfig)
+ c2, err := mergeConfigFromDir(tempDir, defaultConfig)
require.NoError(t, err)
if relay {
require.True(t, c2.Archival, "Relay should be archival")
} else {
require.False(t, c2.Archival, "Non-relay should still be non-archival")
}
-
- os.RemoveAll("testdir")
}
-func TestConfigExampleIsCorrect(t *testing.T) {
+func TestLocal_ConfigExampleIsCorrect(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
@@ -261,7 +251,7 @@ func loadWithoutDefaults(cfg Local) (Local, error) {
return cfg, err
}
-func TestConfigMigrate(t *testing.T) {
+func TestLocal_ConfigMigrate(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
@@ -288,7 +278,7 @@ func TestConfigMigrate(t *testing.T) {
a.NotEqual(defaultLocal, c0Modified)
}
-func TestConfigMigrateFromDisk(t *testing.T) {
+func TestLocal_ConfigMigrateFromDisk(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
@@ -311,7 +301,7 @@ func TestConfigMigrateFromDisk(t *testing.T) {
}
// Verify that nobody is changing the shipping default configurations
-func TestConfigInvariant(t *testing.T) {
+func TestLocal_ConfigInvariant(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
@@ -328,8 +318,9 @@ func TestConfigInvariant(t *testing.T) {
}
}
-func TestConfigLatestVersion(t *testing.T) {
+func TestLocal_ConfigLatestVersion(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
a := require.New(t)
@@ -391,6 +382,7 @@ func TestConsensusLatestVersion(t *testing.T) {
func TestLocal_DNSBootstrapArray(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
type fields struct {
DNSBootstrapID string
@@ -434,6 +426,7 @@ func TestLocal_DNSBootstrapArray(t *testing.T) {
func TestLocal_DNSBootstrap(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
type fields struct {
DNSBootstrapID string
@@ -480,8 +473,9 @@ func TestLocal_DNSBootstrap(t *testing.T) {
}
}
-func TestLocalStructTags(t *testing.T) {
+func TestLocal_StructTags(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
localType := reflect.TypeOf(Local{})
@@ -523,8 +517,9 @@ func TestLocalStructTags(t *testing.T) {
}
}
-func TestGetVersionedDefaultLocalConfig(t *testing.T) {
+func TestLocal_GetVersionedDefaultLocalConfig(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
for i := uint32(0); i < getLatestConfigVersion(); i++ {
localVersion := getVersionedDefaultLocalConfig(i)
@@ -533,8 +528,9 @@ func TestGetVersionedDefaultLocalConfig(t *testing.T) {
}
// TestLocalVersionField - ensures the Version contains only versions tags, the versions are all contiguous, and that no non-version tags are included there.
-func TestLocalVersionField(t *testing.T) {
+func TestLocal_VersionField(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
localType := reflect.TypeOf(Local{})
field, ok := localType.FieldByName("Version")
@@ -553,8 +549,9 @@ func TestLocalVersionField(t *testing.T) {
require.Equal(t, expectedTag, string(field.Tag))
}
-func TestGetNonDefaultConfigValues(t *testing.T) {
+func TestLocal_GetNonDefaultConfigValues(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
cfg := GetDefaultLocal()
@@ -582,6 +579,9 @@ func TestGetNonDefaultConfigValues(t *testing.T) {
}
func TestLocal_TxFiltering(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
cfg := GetDefaultLocal()
// ensure the default
@@ -604,3 +604,62 @@ func TestLocal_TxFiltering(t *testing.T) {
require.True(t, cfg.TxFilterRawMsgEnabled())
require.True(t, cfg.TxFilterCanonicalEnabled())
}
+
+func TestLocal_IsGossipServer(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ cfg := GetDefaultLocal()
+ require.False(t, cfg.IsGossipServer())
+
+ cfg.NetAddress = ":4160"
+ require.True(t, cfg.IsGossipServer())
+}
+
+func TestLocal_RecalculateConnectionLimits(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ var tests = []struct {
+ maxFDs uint64
+ reservedIn uint64
+ restSoftIn uint64
+ restHardIn uint64
+ incomingIn int
+
+ updated bool
+ restSoftExp uint64
+ restHardExp uint64
+ incomingExp int
+ }{
+ {100, 10, 20, 40, 50, false, 20, 40, 50}, // no change
+ {100, 10, 20, 50, 50, true, 20, 40, 50}, // borrow from rest
+ {100, 10, 25, 50, 50, true, 25, 40, 50}, // borrow from rest
+ {100, 10, 50, 50, 50, true, 40, 40, 50}, // borrow from rest, update soft
+ {100, 10, 9, 19, 81, true, 9, 10, 80}, // borrow from both rest and incoming
+ {100, 10, 10, 20, 80, true, 10, 10, 80}, // borrow from both rest and incoming
+ {100, 50, 10, 30, 40, true, 10, 10, 40}, // borrow from both rest and incoming
+ {100, 90, 10, 30, 40, true, 10, 10, 0}, // borrow from both rest and incoming, clear incoming
+ {4096, 256, 1024, 2048, 2400, true, 1024, 1440, 2400}, // real numbers
+ {5000, 256, 1024, 2048, 2400, false, 1024, 2048, 2400}, // real numbers
+ }
+
+ for i, test := range tests {
+ test := test
+ t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+ t.Parallel()
+
+ c := Local{
+ RestConnectionsSoftLimit: test.restSoftIn,
+ RestConnectionsHardLimit: test.restHardIn,
+ IncomingConnectionsLimit: test.incomingIn,
+ }
+ requireFDs := test.reservedIn + test.restHardIn + uint64(test.incomingIn)
+ res := c.AdjustConnectionLimits(requireFDs, test.maxFDs)
+ require.Equal(t, test.updated, res)
+ require.Equal(t, test.restSoftExp, c.RestConnectionsSoftLimit)
+ require.Equal(t, test.restHardExp, c.RestConnectionsHardLimit)
+ require.Equal(t, test.incomingExp, c.IncomingConnectionsLimit)
+ })
+ }
+}
diff --git a/config/consensus.go b/config/consensus.go
index bd67aa340..4f1ac8673 100644
--- a/config/consensus.go
+++ b/config/consensus.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/config/consensus_test.go b/config/consensus_test.go
index dd77ed499..98b5e874c 100644
--- a/config/consensus_test.go
+++ b/config/consensus_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/config/defaultsGenerator/defaultsGenerator.go b/config/defaultsGenerator/defaultsGenerator.go
index 4ee4a1671..9c3365f89 100644
--- a/config/defaultsGenerator/defaultsGenerator.go
+++ b/config/defaultsGenerator/defaultsGenerator.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/config/keyfile.go b/config/keyfile.go
index 28aea1f22..0f158e1fd 100644
--- a/config/keyfile.go
+++ b/config/keyfile.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/config/localTemplate.go b/config/localTemplate.go
index 6be2f0e1a..12df3915b 100644
--- a/config/localTemplate.go
+++ b/config/localTemplate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -41,7 +41,7 @@ type Local struct {
// Version tracks the current version of the defaults so we can migrate old -> new
// This is specifically important whenever we decide to change the default value
// for an existing parameter. This field tag must be updated any time we add a new version.
- Version uint32 `version[0]:"0" version[1]:"1" version[2]:"2" version[3]:"3" version[4]:"4" version[5]:"5" version[6]:"6" version[7]:"7" version[8]:"8" version[9]:"9" version[10]:"10" version[11]:"11" version[12]:"12" version[13]:"13" version[14]:"14" version[15]:"15" version[16]:"16" version[17]:"17" version[18]:"18" version[19]:"19" version[20]:"20" version[21]:"21" version[22]:"22" version[23]:"23" version[24]:"24" version[25]:"25" version[26]:"26"`
+ Version uint32 `version[0]:"0" version[1]:"1" version[2]:"2" version[3]:"3" version[4]:"4" version[5]:"5" version[6]:"6" version[7]:"7" version[8]:"8" version[9]:"9" version[10]:"10" version[11]:"11" version[12]:"12" version[13]:"13" version[14]:"14" version[15]:"15" version[16]:"16" version[17]:"17" version[18]:"18" version[19]:"19" version[20]:"20" version[21]:"21" version[22]:"22" version[23]:"23" version[24]:"24" version[25]:"25" version[26]:"26" version[27]:"27"`
// environmental (may be overridden)
// When enabled, stores blocks indefinitely, otherwise, only the most recent blocks
@@ -59,7 +59,7 @@ type Local struct {
// what we should tell people to connect to
PublicAddress string `version[0]:""`
- MaxConnectionsPerIP int `version[3]:"30"`
+ MaxConnectionsPerIP int `version[3]:"30" version[27]:"15"`
// 0 == disable
PeerPingPeriodSeconds int `version[0]:"0"`
@@ -72,11 +72,12 @@ type Local struct {
BaseLoggerDebugLevel uint32 `version[0]:"1" version[1]:"4"`
// if this is 0, do not produce agreement.cadaver
CadaverSizeTarget uint64 `version[0]:"1073741824" version[24]:"0"`
+ CadaverDirectory string `version[27]:""`
// IncomingConnectionsLimit specifies the max number of long-lived incoming
// connections. 0 means no connections allowed. Must be non-negative.
- // Estimating 5MB per incoming connection, 5MB*800 = 4GB
- IncomingConnectionsLimit int `version[0]:"-1" version[1]:"10000" version[17]:"800"`
+ // Estimating 1.5MB per incoming connection, 1.5MB*2400 = 3.6GB
+ IncomingConnectionsLimit int `version[0]:"-1" version[1]:"10000" version[17]:"800" version[27]:"2400"`
// BroadcastConnectionsLimit specifies the number of connections that
// will receive broadcast (gossip) messages from this node. If the
@@ -102,6 +103,8 @@ type Local struct {
// that RLIMIT_NOFILE >= IncomingConnectionsLimit + RestConnectionsHardLimit +
// ReservedFDs. ReservedFDs are meant to leave room for short-lived FDs like
// DNS queries, SQLite files, etc. This parameter shouldn't be changed.
+ // If RLIMIT_NOFILE < IncomingConnectionsLimit + RestConnectionsHardLimit + ReservedFDs
+ // then either RestConnectionsHardLimit or IncomingConnectionsLimit decreased.
ReservedFDs uint64 `version[2]:"256"`
// local server
@@ -161,6 +164,20 @@ type Local struct {
SuggestedFeeBlockHistory int `version[0]:"3"`
+ // TxBacklogServiceRateWindowSeconds is the window size used to determine the service rate of the txBacklog
+ TxBacklogServiceRateWindowSeconds int `version[27]:"10"`
+
+ // TxBacklogReservedCapacityPerPeer determines how much dedicated serving capacity the TxBacklog gives each peer
+ TxBacklogReservedCapacityPerPeer int `version[27]:"20"`
+
+ // EnableTxBacklogRateLimiting controls if a rate limiter and congestion manager shouild be attached to the tx backlog enqueue process
+ // if enabled, the over-all TXBacklog Size will be larger by MAX_PEERS*TxBacklogReservedCapacityPerPeer
+ EnableTxBacklogRateLimiting bool `version[27]:"false"`
+
+ // TxBacklogSize is the queue size used for receiving transactions. default of 26000 to approximate 1 block of transactions
+ // if EnableTxBacklogRateLimiting enabled, the over-all size will be larger by MAX_PEERS*TxBacklogReservedCapacityPerPeer
+ TxBacklogSize int `version[27]:"26000"`
+
// TxPoolSize is the number of transactions that fit in the transaction pool
TxPoolSize int `version[0]:"50000" version[5]:"15000" version[23]:"75000"`
@@ -247,6 +264,10 @@ type Local struct {
// telemetry ( when enabled ). Defined in seconds.
PeerConnectionsUpdateInterval int `version[5]:"3600"`
+ // HeartbeatUpdateInterval defines the interval at which the heartbeat information is being sent to the
+ // telemetry ( when enabled ). Defined in seconds. Minimum value is 60.
+ HeartbeatUpdateInterval int `version[27]:"600"`
+
// EnableProfiler enables the go pprof endpoints, should be false if
// the algod api will be exposed to untrusted individuals
EnableProfiler bool `version[0]:"false"`
@@ -441,13 +462,13 @@ type Local struct {
MaxAPIResourcesPerAccount uint64 `version[21]:"100000"`
// AgreementIncomingVotesQueueLength sets the size of the buffer holding incoming votes.
- AgreementIncomingVotesQueueLength uint64 `version[21]:"10000"`
+ AgreementIncomingVotesQueueLength uint64 `version[21]:"10000" version[27]:"20000"`
// AgreementIncomingProposalsQueueLength sets the size of the buffer holding incoming proposals.
- AgreementIncomingProposalsQueueLength uint64 `version[21]:"25"`
+ AgreementIncomingProposalsQueueLength uint64 `version[21]:"25" version[27]:"50"`
// AgreementIncomingBundlesQueueLength sets the size of the buffer holding incoming bundles.
- AgreementIncomingBundlesQueueLength uint64 `version[21]:"7"`
+ AgreementIncomingBundlesQueueLength uint64 `version[21]:"7" version[27]:"15"`
// MaxAcctLookback sets the maximum lookback range for account states,
// i.e. the ledger can answer account states questions for the range Latest-MaxAcctLookback...Latest
@@ -467,6 +488,10 @@ type Local struct {
// 0x01 (txFilterRawMsg) - check for raw tx message duplicates
// 0x02 (txFilterCanonical) - check for canonical tx group duplicates
TxIncomingFilteringFlags uint32 `version[26]:"1"`
+
+ // EnableExperimentalAPI enables experimental API endpoint. Note that these endpoints have no
+ // guarantees in terms of functionality or future support.
+ EnableExperimentalAPI bool `version[26]:"false"`
}
// DNSBootstrapArray returns an array of one or more DNS Bootstrap identifiers
@@ -565,3 +590,37 @@ func (cfg Local) TxFilterRawMsgEnabled() bool {
func (cfg Local) TxFilterCanonicalEnabled() bool {
return cfg.TxIncomingFilteringFlags&txFilterCanonical != 0
}
+
+// IsGossipServer returns true if NetAddress is set and this node supposed
+// to start websocket server
+func (cfg Local) IsGossipServer() bool {
+ return cfg.NetAddress != ""
+}
+
+// AdjustConnectionLimits updates RestConnectionsSoftLimit, RestConnectionsHardLimit, IncomingConnectionsLimit
+// if requiredFDs greater than maxFDs
+func (cfg *Local) AdjustConnectionLimits(requiredFDs, maxFDs uint64) bool {
+ if maxFDs >= requiredFDs {
+ return false
+ }
+ const reservedRESTConns = 10
+ diff := requiredFDs - maxFDs
+
+ if cfg.RestConnectionsHardLimit <= diff+reservedRESTConns {
+ restDelta := diff + reservedRESTConns - cfg.RestConnectionsHardLimit
+ cfg.RestConnectionsHardLimit = reservedRESTConns
+ if cfg.IncomingConnectionsLimit > int(restDelta) {
+ cfg.IncomingConnectionsLimit -= int(restDelta)
+ } else {
+ cfg.IncomingConnectionsLimit = 0
+ }
+ } else {
+ cfg.RestConnectionsHardLimit -= diff
+ }
+
+ if cfg.RestConnectionsSoftLimit > cfg.RestConnectionsHardLimit {
+ cfg.RestConnectionsSoftLimit = cfg.RestConnectionsHardLimit
+ }
+
+ return true
+}
diff --git a/config/local_defaults.go b/config/local_defaults.go
index 412c4a3dd..18604556f 100644
--- a/config/local_defaults.go
+++ b/config/local_defaults.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -20,17 +20,18 @@
package config
var defaultLocal = Local{
- Version: 26,
+ Version: 27,
AccountUpdatesStatsInterval: 5000000000,
AccountsRebuildSynchronousMode: 1,
- AgreementIncomingBundlesQueueLength: 7,
- AgreementIncomingProposalsQueueLength: 25,
- AgreementIncomingVotesQueueLength: 10000,
+ AgreementIncomingBundlesQueueLength: 15,
+ AgreementIncomingProposalsQueueLength: 50,
+ AgreementIncomingVotesQueueLength: 20000,
AnnounceParticipationKey: true,
Archival: false,
BaseLoggerDebugLevel: 4,
BlockServiceCustomFallbackEndpoints: "",
BroadcastConnectionsLimit: -1,
+ CadaverDirectory: "",
CadaverSizeTarget: 0,
CatchpointFileHistoryLength: 365,
CatchpointInterval: 10000,
@@ -59,6 +60,7 @@ var defaultLocal = Local{
EnableBlockServiceFallbackToArchiver: true,
EnableCatchupFromArchiveServers: false,
EnableDeveloperAPI: false,
+ EnableExperimentalAPI: false,
EnableGossipBlockService: true,
EnableIncomingMessageFilter: false,
EnableLedgerService: false,
@@ -70,6 +72,7 @@ var defaultLocal = Local{
EnableRequestLogger: false,
EnableRuntimeMetrics: false,
EnableTopAccountsReporting: false,
+ EnableTxBacklogRateLimiting: false,
EnableUsageLog: false,
EnableVerbosedTransactionSyncLogging: false,
EndpointAddress: "127.0.0.1:0",
@@ -77,7 +80,8 @@ var defaultLocal = Local{
ForceFetchTransactions: false,
ForceRelayMessages: false,
GossipFanout: 4,
- IncomingConnectionsLimit: 800,
+ HeartbeatUpdateInterval: 600,
+ IncomingConnectionsLimit: 2400,
IncomingMessageFilterBucketCount: 5,
IncomingMessageFilterBucketSize: 512,
IsIndexerActive: false,
@@ -89,7 +93,7 @@ var defaultLocal = Local{
MaxAPIResourcesPerAccount: 100000,
MaxAcctLookback: 4,
MaxCatchpointDownloadDuration: 7200000000000,
- MaxConnectionsPerIP: 30,
+ MaxConnectionsPerIP: 15,
MinCatchpointFileDownloadBytesPerSecond: 20480,
NetAddress: "",
NetworkMessageTraceServer: "",
@@ -119,6 +123,9 @@ var defaultLocal = Local{
TelemetryToLog: true,
TransactionSyncDataExchangeRate: 0,
TransactionSyncSignificantMessageThreshold: 0,
+ TxBacklogReservedCapacityPerPeer: 20,
+ TxBacklogServiceRateWindowSeconds: 10,
+ TxBacklogSize: 26000,
TxIncomingFilteringFlags: 1,
TxPoolExponentialIncreaseFactor: 2,
TxPoolSize: 75000,
diff --git a/config/migrate.go b/config/migrate.go
index 405314c62..35b4ec82f 100644
--- a/config/migrate.go
+++ b/config/migrate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/config/version.go b/config/version.go
index 6d1a91c3f..4c91e271f 100644
--- a/config/version.go
+++ b/config/version.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -33,7 +33,7 @@ const VersionMajor = 3
// VersionMinor is the Minor semantic version number (x.#.z) - changed when backwards-compatible features are introduced.
// Not enforced until after initial public release (x > 0).
-const VersionMinor = 13
+const VersionMinor = 14
// Version is the type holding our full version information.
type Version struct {
diff --git a/crypto/batchverifier.go b/crypto/batchverifier.go
index a358d65e3..9c14771ba 100644
--- a/crypto/batchverifier.go
+++ b/crypto/batchverifier.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -53,7 +53,7 @@ const minBatchVerifierAlloc = 16
// Batch verifications errors
var (
- ErrBatchVerificationFailed = errors.New("At least one signature didn't pass verification")
+ ErrBatchHasFailedSigs = errors.New("At least one signature didn't pass verification")
)
//export ed25519_randombytes_unsafe
@@ -104,8 +104,8 @@ func (b *BatchVerifier) expand() {
b.signatures = signatures
}
-// getNumberOfEnqueuedSignatures returns the number of signatures current enqueue onto the batch verifier object
-func (b *BatchVerifier) getNumberOfEnqueuedSignatures() int {
+// GetNumberOfEnqueuedSignatures returns the number of signatures currently enqueued into the BatchVerifier
+func (b *BatchVerifier) GetNumberOfEnqueuedSignatures() int {
return len(b.messages)
}
@@ -120,18 +120,18 @@ func (b *BatchVerifier) Verify() error {
// if some signatures are invalid, true will be set in failed at the corresponding indexes, and
// ErrBatchVerificationFailed for err
func (b *BatchVerifier) VerifyWithFeedback() (failed []bool, err error) {
- if b.getNumberOfEnqueuedSignatures() == 0 {
+ if b.GetNumberOfEnqueuedSignatures() == 0 {
return nil, nil
}
- var messages = make([][]byte, b.getNumberOfEnqueuedSignatures())
- for i, m := range b.messages {
- messages[i] = HashRep(m)
+ var messages = make([][]byte, b.GetNumberOfEnqueuedSignatures())
+ for i := range b.messages {
+ messages[i] = HashRep(b.messages[i])
}
allValid, failed := batchVerificationImpl(messages, b.publicKeys, b.signatures)
if allValid {
return failed, nil
}
- return failed, ErrBatchVerificationFailed
+ return failed, ErrBatchHasFailedSigs
}
// batchVerificationImpl invokes the ed25519 batch verification algorithm.
diff --git a/crypto/batchverifier_test.go b/crypto/batchverifier_test.go
index 7c5455703..c572503ff 100644
--- a/crypto/batchverifier_test.go
+++ b/crypto/batchverifier_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -17,6 +17,7 @@
package crypto
import (
+ "fmt"
"math/rand"
"runtime"
"testing"
@@ -64,7 +65,7 @@ func TestBatchVerifierBulk(t *testing.T) {
sig := sigSecrets.Sign(msg)
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)
}
- require.Equal(t, n, bv.getNumberOfEnqueuedSignatures())
+ require.Equal(t, n, bv.GetNumberOfEnqueuedSignatures())
require.NoError(t, bv.Verify())
}
@@ -121,6 +122,67 @@ func BenchmarkBatchVerifier(b *testing.B) {
require.NoError(b, bv.Verify())
}
+// BenchmarkBatchVerifierBig with b.N over 1000 will report the expected performance
+// gain as the batchsize increases. All sigs are valid.
+func BenchmarkBatchVerifierBig(b *testing.B) {
+ c := makeCurve25519Secret()
+ for batchSize := 1; batchSize <= 96; batchSize++ {
+ bv := MakeBatchVerifierWithHint(batchSize)
+ for i := 0; i < batchSize; i++ {
+ str := randString()
+ bv.EnqueueSignature(c.SignatureVerifier, str, c.Sign(str))
+ }
+ b.Run(fmt.Sprintf("running batchsize %d", batchSize), func(b *testing.B) {
+ totalTransactions := b.N
+ count := totalTransactions / batchSize
+ if count*batchSize < totalTransactions {
+ count++
+ }
+ for x := 0; x < count; x++ {
+ require.NoError(b, bv.Verify())
+ }
+ })
+ }
+}
+
+// BenchmarkBatchVerifierBigWithInvalid builds over BenchmarkBatchVerifierBig by introducing
+// invalid sigs to even numbered batch sizes. This shows the impact of invalid sigs on the
+// performance. Basically, all the gains from batching disappear.
+func BenchmarkBatchVerifierBigWithInvalid(b *testing.B) {
+ c := makeCurve25519Secret()
+ badSig := Signature{}
+ for batchSize := 1; batchSize <= 96; batchSize++ {
+ bv := MakeBatchVerifierWithHint(batchSize)
+ for i := 0; i < batchSize; i++ {
+ str := randString()
+ if batchSize%2 == 0 && (i == 0 || rand.Float32() < 0.1) {
+ bv.EnqueueSignature(c.SignatureVerifier, str, badSig)
+ } else {
+ bv.EnqueueSignature(c.SignatureVerifier, str, c.Sign(str))
+ }
+ }
+ b.Run(fmt.Sprintf("running batchsize %d", batchSize), func(b *testing.B) {
+ totalTransactions := b.N
+ count := totalTransactions / batchSize
+ if count*batchSize < totalTransactions {
+ count++
+ }
+ for x := 0; x < count; x++ {
+ failed, err := bv.VerifyWithFeedback()
+ if err != nil {
+ for i, f := range failed {
+ if bv.signatures[i] == badSig {
+ require.True(b, f)
+ } else {
+ require.False(b, f)
+ }
+ }
+ }
+ }
+ })
+ }
+}
+
func TestEmpty(t *testing.T) {
partitiontest.PartitionTest(t)
bv := MakeBatchVerifier()
@@ -155,10 +217,10 @@ func TestBatchVerifierIndividualResults(t *testing.T) {
}
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)
}
- require.Equal(t, n, bv.getNumberOfEnqueuedSignatures())
+ require.Equal(t, n, bv.GetNumberOfEnqueuedSignatures())
failed, err := bv.VerifyWithFeedback()
if hasBadSig {
- require.ErrorIs(t, err, ErrBatchVerificationFailed)
+ require.ErrorIs(t, err, ErrBatchHasFailedSigs)
} else {
require.NoError(t, err)
}
@@ -185,10 +247,10 @@ func TestBatchVerifierIndividualResultsAllValid(t *testing.T) {
sig := sigSecrets.Sign(msg)
bv.EnqueueSignature(sigSecrets.SignatureVerifier, msg, sig)
}
- require.Equal(t, n, bv.getNumberOfEnqueuedSignatures())
+ require.Equal(t, n, bv.GetNumberOfEnqueuedSignatures())
failed, err := bv.VerifyWithFeedback()
require.NoError(t, err)
- require.Equal(t, bv.getNumberOfEnqueuedSignatures(), len(failed))
+ require.Equal(t, bv.GetNumberOfEnqueuedSignatures(), len(failed))
for _, f := range failed {
require.False(t, f)
}
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index 86c65f909..f0e8b1d0e 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/cryptoerror.go b/crypto/cryptoerror.go
index 26636dd88..3282f8d4a 100644
--- a/crypto/cryptoerror.go
+++ b/crypto/cryptoerror.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/curve25519.go b/crypto/curve25519.go
index 91dc5d63e..e409fc794 100644
--- a/crypto/curve25519.go
+++ b/crypto/curve25519.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/curve25519_test.go b/crypto/curve25519_test.go
index 27d153b9d..acd152cba 100644
--- a/crypto/curve25519_test.go
+++ b/crypto/curve25519_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/digest.go b/crypto/digest.go
index a65fa4d39..27bef9a3c 100644
--- a/crypto/digest.go
+++ b/crypto/digest.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/encoding_test.go b/crypto/encoding_test.go
index 515f6a3ef..527a56d2c 100644
--- a/crypto/encoding_test.go
+++ b/crypto/encoding_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/falconWrapper.go b/crypto/falconWrapper.go
index f24bc9433..0a2c000b9 100644
--- a/crypto/falconWrapper.go
+++ b/crypto/falconWrapper.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/falconWrapper_test.go b/crypto/falconWrapper_test.go
index 20b5441ee..45adb124a 100644
--- a/crypto/falconWrapper_test.go
+++ b/crypto/falconWrapper_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -17,10 +17,11 @@
package crypto
import (
+ "testing"
+
"github.com/algorand/falcon"
"github.com/algorand/go-algorand/test/partitiontest"
"github.com/stretchr/testify/require"
- "testing"
)
func TestSignAndVerifyFalcon(t *testing.T) {
diff --git a/crypto/hashes.go b/crypto/hashes.go
index 67ccdafc0..b1a541bba 100644
--- a/crypto/hashes.go
+++ b/crypto/hashes.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/hashes_test.go b/crypto/hashes_test.go
index a3054d298..dd4b8c3bd 100644
--- a/crypto/hashes_test.go
+++ b/crypto/hashes_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/memcpy_chk_windows.c b/crypto/memcpy_chk_windows.c
index f14eee0fe..d79118cdf 100644
--- a/crypto/memcpy_chk_windows.c
+++ b/crypto/memcpy_chk_windows.c
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/array.go b/crypto/merklearray/array.go
index 6b783d860..4e04b5338 100644
--- a/crypto/merklearray/array.go
+++ b/crypto/merklearray/array.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/layer.go b/crypto/merklearray/layer.go
index b1a9281fc..45aa93ff8 100644
--- a/crypto/merklearray/layer.go
+++ b/crypto/merklearray/layer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/merkle.go b/crypto/merklearray/merkle.go
index 803eb5a25..b4591bf6b 100644
--- a/crypto/merklearray/merkle.go
+++ b/crypto/merklearray/merkle.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/merkle_test.go b/crypto/merklearray/merkle_test.go
index 0f87474e5..9a1a5c0fd 100644
--- a/crypto/merklearray/merkle_test.go
+++ b/crypto/merklearray/merkle_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/partial.go b/crypto/merklearray/partial.go
index c8e62f4dd..a1d0861dc 100644
--- a/crypto/merklearray/partial.go
+++ b/crypto/merklearray/partial.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/proof.go b/crypto/merklearray/proof.go
index 2670f69f1..e64479a96 100644
--- a/crypto/merklearray/proof.go
+++ b/crypto/merklearray/proof.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/proof_test.go b/crypto/merklearray/proof_test.go
index 4645dccb2..d3683689f 100644
--- a/crypto/merklearray/proof_test.go
+++ b/crypto/merklearray/proof_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/vectorCommitmentArray.go b/crypto/merklearray/vectorCommitmentArray.go
index a9f9b3580..c11e295ae 100644
--- a/crypto/merklearray/vectorCommitmentArray.go
+++ b/crypto/merklearray/vectorCommitmentArray.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/vectorCommitmentArray_test.go b/crypto/merklearray/vectorCommitmentArray_test.go
index d3a4ae065..e3887880d 100644
--- a/crypto/merklearray/vectorCommitmentArray_test.go
+++ b/crypto/merklearray/vectorCommitmentArray_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklearray/worker.go b/crypto/merklearray/worker.go
index e7a4dc222..2a8059336 100644
--- a/crypto/merklearray/worker.go
+++ b/crypto/merklearray/worker.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/committablePublicKeys.go b/crypto/merklesignature/committablePublicKeys.go
index 7401c67ef..fd4a9f26a 100644
--- a/crypto/merklesignature/committablePublicKeys.go
+++ b/crypto/merklesignature/committablePublicKeys.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -34,7 +34,7 @@ type (
keyLifetime uint64
}
- // CommittablePublicKey is used to create a binary representation of public keys in the merkle
+ // CommittablePublicKey is used to create a binary representation of public keys in the merkle
// signature scheme.
CommittablePublicKey struct {
VerifyingKey crypto.FalconVerifier
diff --git a/crypto/merklesignature/committablePublicKeys_test.go b/crypto/merklesignature/committablePublicKeys_test.go
index a6884cc59..08108b302 100644
--- a/crypto/merklesignature/committablePublicKeys_test.go
+++ b/crypto/merklesignature/committablePublicKeys_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/const.go b/crypto/merklesignature/const.go
index 767f14aae..52d98390f 100644
--- a/crypto/merklesignature/const.go
+++ b/crypto/merklesignature/const.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/kats_test.go b/crypto/merklesignature/kats_test.go
index bc61ec47b..610cdbab9 100644
--- a/crypto/merklesignature/kats_test.go
+++ b/crypto/merklesignature/kats_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/keysBuilder.go b/crypto/merklesignature/keysBuilder.go
index 93553e7d5..d284ca29b 100644
--- a/crypto/merklesignature/keysBuilder.go
+++ b/crypto/merklesignature/keysBuilder.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/keysBuilder_test.go b/crypto/merklesignature/keysBuilder_test.go
index ec9e487fe..24f426e70 100644
--- a/crypto/merklesignature/keysBuilder_test.go
+++ b/crypto/merklesignature/keysBuilder_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/merkleSignatureScheme.go b/crypto/merklesignature/merkleSignatureScheme.go
index b2763d496..f11ecb920 100644
--- a/crypto/merklesignature/merkleSignatureScheme.go
+++ b/crypto/merklesignature/merkleSignatureScheme.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/merkleSignatureScheme_test.go b/crypto/merklesignature/merkleSignatureScheme_test.go
index db4f4e6e4..9b56cb11d 100644
--- a/crypto/merklesignature/merkleSignatureScheme_test.go
+++ b/crypto/merklesignature/merkleSignatureScheme_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/persistentMerkleSignatureScheme.go b/crypto/merklesignature/persistentMerkleSignatureScheme.go
index 2ece3380d..c862dcb96 100644
--- a/crypto/merklesignature/persistentMerkleSignatureScheme.go
+++ b/crypto/merklesignature/persistentMerkleSignatureScheme.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/persistentMerkleSignatureScheme_test.go b/crypto/merklesignature/persistentMerkleSignatureScheme_test.go
index 970daf903..5885f1cf6 100644
--- a/crypto/merklesignature/persistentMerkleSignatureScheme_test.go
+++ b/crypto/merklesignature/persistentMerkleSignatureScheme_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/posdivs.go b/crypto/merklesignature/posdivs.go
index 9ce88d53e..ac152234f 100644
--- a/crypto/merklesignature/posdivs.go
+++ b/crypto/merklesignature/posdivs.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merklesignature/posdivs_test.go b/crypto/merklesignature/posdivs_test.go
index 579a1c3d5..394c31870 100644
--- a/crypto/merklesignature/posdivs_test.go
+++ b/crypto/merklesignature/posdivs_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/bitset.go b/crypto/merkletrie/bitset.go
index f7f2d78d0..3007372ab 100644
--- a/crypto/merkletrie/bitset.go
+++ b/crypto/merkletrie/bitset.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/bitset_test.go b/crypto/merkletrie/bitset_test.go
index 758c3299e..2fa5a35f9 100644
--- a/crypto/merkletrie/bitset_test.go
+++ b/crypto/merkletrie/bitset_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/cache.go b/crypto/merkletrie/cache.go
index 4375825a2..208397edb 100644
--- a/crypto/merkletrie/cache.go
+++ b/crypto/merkletrie/cache.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/cache_test.go b/crypto/merkletrie/cache_test.go
index 15b14e676..d9c7a23e3 100644
--- a/crypto/merkletrie/cache_test.go
+++ b/crypto/merkletrie/cache_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/committer.go b/crypto/merkletrie/committer.go
index ac6fcd72c..bad5fe7e0 100644
--- a/crypto/merkletrie/committer.go
+++ b/crypto/merkletrie/committer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/committer_test.go b/crypto/merkletrie/committer_test.go
index 815274302..6f8dfb2a7 100644
--- a/crypto/merkletrie/committer_test.go
+++ b/crypto/merkletrie/committer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/node.go b/crypto/merkletrie/node.go
index e4d3a18f3..33c2f673a 100644
--- a/crypto/merkletrie/node.go
+++ b/crypto/merkletrie/node.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/node_test.go b/crypto/merkletrie/node_test.go
index 76121b5ae..1495a8e9c 100644
--- a/crypto/merkletrie/node_test.go
+++ b/crypto/merkletrie/node_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/trie.go b/crypto/merkletrie/trie.go
index 22c03e1ce..7a214e709 100644
--- a/crypto/merkletrie/trie.go
+++ b/crypto/merkletrie/trie.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/merkletrie/trie_test.go b/crypto/merkletrie/trie_test.go
index fd9b38d98..f71545962 100644
--- a/crypto/merkletrie/trie_test.go
+++ b/crypto/merkletrie/trie_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/multisig.go b/crypto/multisig.go
index 62ec187a2..d6f19bf4a 100644
--- a/crypto/multisig.go
+++ b/crypto/multisig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/multisig_test.go b/crypto/multisig_test.go
index 5035300d2..e7f4a17da 100644
--- a/crypto/multisig_test.go
+++ b/crypto/multisig_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/onetimesig.go b/crypto/onetimesig.go
index 2aaa58bc0..344fd33f7 100644
--- a/crypto/onetimesig.go
+++ b/crypto/onetimesig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/onetimesig_test.go b/crypto/onetimesig_test.go
index af60a3c73..143bb5c16 100644
--- a/crypto/onetimesig_test.go
+++ b/crypto/onetimesig_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/passphrase/errors.go b/crypto/passphrase/errors.go
index 7785ca256..0a50d59c4 100644
--- a/crypto/passphrase/errors.go
+++ b/crypto/passphrase/errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/passphrase/passphrase.go b/crypto/passphrase/passphrase.go
index f90593ce5..0f0d09d34 100644
--- a/crypto/passphrase/passphrase.go
+++ b/crypto/passphrase/passphrase.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/passphrase/passphrase_test.go b/crypto/passphrase/passphrase_test.go
index 3874f57e0..543ed5fd7 100644
--- a/crypto/passphrase/passphrase_test.go
+++ b/crypto/passphrase/passphrase_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/passphrase/wordlist.go b/crypto/passphrase/wordlist.go
index 4da859135..670a72b3d 100644
--- a/crypto/passphrase/wordlist.go
+++ b/crypto/passphrase/wordlist.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/rand.go b/crypto/rand.go
index 729e01c17..99f22e145 100644
--- a/crypto/rand.go
+++ b/crypto/rand.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/rand_test.go b/crypto/rand_test.go
index 5f69fd55c..129f83171 100644
--- a/crypto/rand_test.go
+++ b/crypto/rand_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/builder.go b/crypto/stateproof/builder.go
index 3f85656ab..0e86fa894 100644
--- a/crypto/stateproof/builder.go
+++ b/crypto/stateproof/builder.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/builder_test.go b/crypto/stateproof/builder_test.go
index 780262e85..6938a4b17 100644
--- a/crypto/stateproof/builder_test.go
+++ b/crypto/stateproof/builder_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/coinGenerator.go b/crypto/stateproof/coinGenerator.go
index fa88c5770..1c019c869 100644
--- a/crypto/stateproof/coinGenerator.go
+++ b/crypto/stateproof/coinGenerator.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/coinGenerator_test.go b/crypto/stateproof/coinGenerator_test.go
index 39f3d760c..d15091f6d 100644
--- a/crypto/stateproof/coinGenerator_test.go
+++ b/crypto/stateproof/coinGenerator_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/committableSignatureSlot.go b/crypto/stateproof/committableSignatureSlot.go
index 78aef90ec..7ae0e8ac5 100644
--- a/crypto/stateproof/committableSignatureSlot.go
+++ b/crypto/stateproof/committableSignatureSlot.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/committableSignatureSlot_test.go b/crypto/stateproof/committableSignatureSlot_test.go
index 811c79c4d..b2519cea6 100644
--- a/crypto/stateproof/committableSignatureSlot_test.go
+++ b/crypto/stateproof/committableSignatureSlot_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/const.go b/crypto/stateproof/const.go
index a9dab2813..eefe0d1a9 100644
--- a/crypto/stateproof/const.go
+++ b/crypto/stateproof/const.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/structs.go b/crypto/stateproof/structs.go
index d8e0b6883..7ee599b7a 100644
--- a/crypto/stateproof/structs.go
+++ b/crypto/stateproof/structs.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/verifier.go b/crypto/stateproof/verifier.go
index 892c9d477..67d83068f 100644
--- a/crypto/stateproof/verifier.go
+++ b/crypto/stateproof/verifier.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/verifier_test.go b/crypto/stateproof/verifier_test.go
index 91ee12311..23a5fac3a 100644
--- a/crypto/stateproof/verifier_test.go
+++ b/crypto/stateproof/verifier_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/weights.go b/crypto/stateproof/weights.go
index 8d0bdd13d..aa849ec6a 100644
--- a/crypto/stateproof/weights.go
+++ b/crypto/stateproof/weights.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/stateproof/weights_test.go b/crypto/stateproof/weights_test.go
index d7a694897..5800c49f5 100644
--- a/crypto/stateproof/weights_test.go
+++ b/crypto/stateproof/weights_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/util.go b/crypto/util.go
index 2ee4ee794..aa8dd3cfc 100644
--- a/crypto/util.go
+++ b/crypto/util.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/util_test.go b/crypto/util_test.go
index 281d78a1d..667da0bcd 100644
--- a/crypto/util_test.go
+++ b/crypto/util_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/vrf.go b/crypto/vrf.go
index dfc2fbf1c..bfdf4ec40 100644
--- a/crypto/vrf.go
+++ b/crypto/vrf.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/crypto/vrf_test.go b/crypto/vrf_test.go
index 72c7f68b3..22b3e7527 100644
--- a/crypto/vrf_test.go
+++ b/crypto/vrf_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/Makefile b/daemon/algod/api/Makefile
index 811b18dc6..c4104d9e8 100644
--- a/daemon/algod/api/Makefile
+++ b/daemon/algod/api/Makefile
@@ -2,7 +2,7 @@ GOPATH := $(shell go env GOPATH)
GOPATH1 := $(firstword $(subst :, ,$(GOPATH)))
# `make all` or just `make` should be appropriate for dev work
-all: server/v2/generated/model/types.go server/v2/generated/nonparticipating/public/routes.go server/v2/generated/nonparticipating/private/routes.go server/v2/generated/participating/public/routes.go server/v2/generated/participating/private/routes.go server/v2/generated/data/routes.go
+all: server/v2/generated/model/types.go server/v2/generated/nonparticipating/public/routes.go server/v2/generated/nonparticipating/private/routes.go server/v2/generated/participating/public/routes.go server/v2/generated/participating/private/routes.go server/v2/generated/data/routes.go server/v2/generated/experimental/routes.go
# `make generate` should be able to replace old `generate.sh` script and be appropriate for build system use
generate: oapi-codegen all
@@ -23,6 +23,9 @@ server/v2/generated/participating/private/routes.go: algod.oas3.yml
server/v2/generated/data/routes.go: algod.oas3.yml
$(GOPATH1)/bin/oapi-codegen -config ./server/v2/generated/data/data_routes.yml algod.oas3.yml
+server/v2/generated/experimental/routes.go: algod.oas3.yml
+ $(GOPATH1)/bin/oapi-codegen -config ./server/v2/generated/experimental/experimental_routes.yml algod.oas3.yml
+
server/v2/generated/model/types.go: algod.oas3.yml
$(GOPATH1)/bin/oapi-codegen -config ./server/v2/generated/model/model_types.yml algod.oas3.yml
diff --git a/daemon/algod/api/README.md b/daemon/algod/api/README.md
index b23f77128..482ba2300 100644
--- a/daemon/algod/api/README.md
+++ b/daemon/algod/api/README.md
@@ -20,10 +20,10 @@ Each API in `algod.oas2.json`, except for some pre-existing `common` APIs, shoul
1. Either `public` or `private`. This controls the type of authentication used by the API--the `public` APIs use the
`algod.token` token, while the `private` APIs use the admin token, found in `algod.admin.token` within the algod data
directory.
-2. The type, or group, of API. This is currently `participating`, `nonparticipating`, or `data`, but may expand in the
-future to encompass different sets of APIs such as `experimental` APIs. Additional APIs should be added to one of the
-existing sets of tags based on its use case--unless you intend to create a new group in which case you will need to
-additionally ensure your new APIs are registered.
+2. The type, or group, of API. This is currently `participating`, `nonparticipating`, `data`, or `experimental`, but
+may expand in the future to encompass different sets of APIs. Additional APIs should be added to one of the existing
+sets of tags based on its use case--unless you intend to create a new group in which case you will need to additionally
+ensure your new APIs are registered.
For backwards compatibility, the default set of APIs registered will always be `participating` and `nonparticipating`
APIs.
@@ -38,6 +38,8 @@ participation keys, the agreement service, etc.
* A special set of APIs which require manipulating the node state in order to provide additional data about the node state
at some predefined granularity. For example, SetSyncRound and GetLedgerStateDelta used together control and expose StateDelta objects
containing per-round ledger differences that get compacted when actually written to the ledger DB.
+* `experimental`
+ * APIs which are still in development and not ready to be generally released.
## What codegen tool is used?
diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json
index ce1c9f587..b2bd99e36 100644
--- a/daemon/algod/api/algod.oas2.json
+++ b/daemon/algod/api/algod.oas2.json
@@ -1088,7 +1088,7 @@
"schemes": [
"http"
],
- "summary": "Gets the node status after waiting for the given round.",
+ "summary": "Gets the node status after waiting for a round after the given round.",
"operationId": "WaitForBlock",
"parameters": [
{
@@ -1157,7 +1157,7 @@
"schemes": [
"http"
],
- "summary": "Broadcasts a raw transaction to the network.",
+ "summary": "Broadcasts a raw transaction or transaction group to the network.",
"operationId": "RawTransaction",
"parameters": [
{
@@ -1205,6 +1205,69 @@
}
}
},
+ "/v2/transactions/simulate": {
+ "post": {
+ "tags": [
+ "public",
+ "experimental"
+ ],
+ "consumes": [
+ "application/x-binary"
+ ],
+ "produces": [
+ "application/msgpack"
+ ],
+ "schemes": [
+ "http"
+ ],
+ "summary": "Simulates a raw transaction or transaction group as it would be evaluated on the network. WARNING: This endpoint is experimental and under active development. There are no guarantees in terms of functionality or future support.",
+ "operationId": "SimulateTransaction",
+ "parameters": [
+ {
+ "description": "The byte encoded transaction to simulate",
+ "name": "rawtxn",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "binary"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/responses/SimulationResponse"
+ },
+ "400": {
+ "description": "Bad Request - Malformed Algorand transaction",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
+ "401": {
+ "description": "Invalid API Token",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
+ "500": {
+ "description": "Internal Error",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
+ "503": {
+ "description": "Service Temporarily Unavailable",
+ "schema": {
+ "$ref": "#/definitions/ErrorResponse"
+ }
+ },
+ "default": {
+ "description": "Unknown Error"
+ }
+ }
+ }
+ },
"/v2/transactions/params": {
"get": {
"tags": [
@@ -4054,6 +4117,38 @@
"catchpoint-acquired-blocks": {
"description": "The number of blocks that have already been obtained by the node as part of the catchup",
"type": "integer"
+ },
+ "upgrade-delay": {
+ "description": "Upgrade delay",
+ "type": "integer"
+ },
+ "upgrade-node-vote": {
+ "description": "This node's upgrade vote",
+ "type": "boolean"
+ },
+ "upgrade-votes-required": {
+ "description": "Yes votes required for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-votes": {
+ "description": "Total votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-yes-votes": {
+ "description": "Yes votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-no-votes": {
+ "description": "No votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-next-protocol-vote-before": {
+ "description": "Next protocol round",
+ "type": "integer"
+ },
+ "upgrade-vote-rounds": {
+ "description": "Total voting ounds for current upgrade",
+ "type": "integer"
}
}
}
@@ -4128,6 +4223,29 @@
}
}
},
+ "SimulationResponse": {
+ "description": "Result of a transaction group simulation.",
+ "tags": [
+ "experimental"
+ ],
+ "schema": {
+ "type": "object",
+ "required": [
+ "failure-message",
+ "missing-signatures"
+ ],
+ "properties": {
+ "failure-message": {
+ "description": "\\[fm\\] Failure message, if the transaction would have failed during a live broadcast.",
+ "type": "string"
+ },
+ "missing-signatures": {
+ "description": "\\[ms\\] Whether any transactions would have failed during a live broadcast because they were missing signatures.",
+ "type": "boolean"
+ }
+ }
+ }
+ },
"SupplyResponse": {
"description": "Supply represents the current supply of MicroAlgos in the system.",
"schema": {
diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml
index bbb5e85b1..fa8d86f2b 100644
--- a/daemon/algod/api/algod.oas3.yml
+++ b/daemon/algod/api/algod.oas3.yml
@@ -613,6 +613,38 @@
"time-since-last-round": {
"description": "TimeSinceLastRound in nanoseconds",
"type": "integer"
+ },
+ "upgrade-delay": {
+ "description": "Upgrade delay",
+ "type": "integer"
+ },
+ "upgrade-next-protocol-vote-before": {
+ "description": "Next protocol round",
+ "type": "integer"
+ },
+ "upgrade-no-votes": {
+ "description": "No votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-node-vote": {
+ "description": "This node's upgrade vote",
+ "type": "boolean"
+ },
+ "upgrade-vote-rounds": {
+ "description": "Total voting ounds for current upgrade",
+ "type": "integer"
+ },
+ "upgrade-votes": {
+ "description": "Total votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-votes-required": {
+ "description": "Yes votes required for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-yes-votes": {
+ "description": "Yes votes cast for consensus upgrade",
+ "type": "integer"
}
},
"required": [
@@ -721,6 +753,30 @@
},
"description": "Transaction ID of the submission."
},
+ "SimulationResponse": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "properties": {
+ "failure-message": {
+ "description": "\\[fm\\] Failure message, if the transaction would have failed during a live broadcast.",
+ "type": "string"
+ },
+ "missing-signatures": {
+ "description": "\\[ms\\] Whether any transactions would have failed during a live broadcast because they were missing signatures.",
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "failure-message",
+ "missing-signatures"
+ ],
+ "type": "object"
+ }
+ }
+ },
+ "description": "Result of a transaction group simulation."
+ },
"StateProofResponse": {
"content": {
"application/json": {
@@ -4770,6 +4826,38 @@
"time-since-last-round": {
"description": "TimeSinceLastRound in nanoseconds",
"type": "integer"
+ },
+ "upgrade-delay": {
+ "description": "Upgrade delay",
+ "type": "integer"
+ },
+ "upgrade-next-protocol-vote-before": {
+ "description": "Next protocol round",
+ "type": "integer"
+ },
+ "upgrade-no-votes": {
+ "description": "No votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-node-vote": {
+ "description": "This node's upgrade vote",
+ "type": "boolean"
+ },
+ "upgrade-vote-rounds": {
+ "description": "Total voting ounds for current upgrade",
+ "type": "integer"
+ },
+ "upgrade-votes": {
+ "description": "Total votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-votes-required": {
+ "description": "Yes votes required for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-yes-votes": {
+ "description": "Yes votes cast for consensus upgrade",
+ "type": "integer"
}
},
"required": [
@@ -4913,6 +5001,38 @@
"time-since-last-round": {
"description": "TimeSinceLastRound in nanoseconds",
"type": "integer"
+ },
+ "upgrade-delay": {
+ "description": "Upgrade delay",
+ "type": "integer"
+ },
+ "upgrade-next-protocol-vote-before": {
+ "description": "Next protocol round",
+ "type": "integer"
+ },
+ "upgrade-no-votes": {
+ "description": "No votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-node-vote": {
+ "description": "This node's upgrade vote",
+ "type": "boolean"
+ },
+ "upgrade-vote-rounds": {
+ "description": "Total voting ounds for current upgrade",
+ "type": "integer"
+ },
+ "upgrade-votes": {
+ "description": "Total votes cast for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-votes-required": {
+ "description": "Yes votes required for consensus upgrade",
+ "type": "integer"
+ },
+ "upgrade-yes-votes": {
+ "description": "Yes votes cast for consensus upgrade",
+ "type": "integer"
}
},
"required": [
@@ -4975,7 +5095,7 @@
"description": "Unknown Error"
}
},
- "summary": "Gets the node status after waiting for the given round.",
+ "summary": "Gets the node status after waiting for a round after the given round.",
"tags": [
"public",
"nonparticipating"
@@ -5346,7 +5466,7 @@
"description": "Unknown Error"
}
},
- "summary": "Broadcasts a raw transaction to the network.",
+ "summary": "Broadcasts a raw transaction or transaction group to the network.",
"tags": [
"public",
"participating"
@@ -5687,6 +5807,99 @@
]
}
},
+ "/v2/transactions/simulate": {
+ "post": {
+ "operationId": "SimulateTransaction",
+ "requestBody": {
+ "content": {
+ "application/x-binary": {
+ "schema": {
+ "format": "binary",
+ "type": "string"
+ }
+ }
+ },
+ "description": "The byte encoded transaction to simulate",
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "content": {
+ "application/msgpack": {
+ "schema": {
+ "properties": {
+ "failure-message": {
+ "description": "\\[fm\\] Failure message, if the transaction would have failed during a live broadcast.",
+ "type": "string"
+ },
+ "missing-signatures": {
+ "description": "\\[ms\\] Whether any transactions would have failed during a live broadcast because they were missing signatures.",
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "failure-message",
+ "missing-signatures"
+ ],
+ "type": "object"
+ }
+ }
+ },
+ "description": "Result of a transaction group simulation."
+ },
+ "400": {
+ "content": {
+ "application/msgpack": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ },
+ "description": "Bad Request - Malformed Algorand transaction"
+ },
+ "401": {
+ "content": {
+ "application/msgpack": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ },
+ "description": "Invalid API Token"
+ },
+ "500": {
+ "content": {
+ "application/msgpack": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ },
+ "description": "Internal Error"
+ },
+ "503": {
+ "content": {
+ "application/msgpack": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ },
+ "description": "Service Temporarily Unavailable"
+ },
+ "default": {
+ "content": {},
+ "description": "Unknown Error"
+ }
+ },
+ "summary": "Simulates a raw transaction or transaction group as it would be evaluated on the network. WARNING: This endpoint is experimental and under active development. There are no guarantees in terms of functionality or future support.",
+ "tags": [
+ "public",
+ "experimental"
+ ],
+ "x-codegen-request-body-name": "rawtxn"
+ }
+ },
"/versions": {
"get": {
"description": "Retrieves the supported API versions, binary build versions, and genesis information.",
diff --git a/daemon/algod/api/client/encoding.go b/daemon/algod/api/client/encoding.go
index 036948c1d..365fb7d50 100644
--- a/daemon/algod/api/client/encoding.go
+++ b/daemon/algod/api/client/encoding.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/client/restClient.go b/daemon/algod/api/client/restClient.go
index a0ac7d1f2..3cf61e903 100644
--- a/daemon/algod/api/client/restClient.go
+++ b/daemon/algod/api/client/restClient.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/common/handlers.go b/daemon/algod/api/server/common/handlers.go
index f9e2c4747..0aa107f42 100644
--- a/daemon/algod/api/server/common/handlers.go
+++ b/daemon/algod/api/server/common/handlers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/common/metrics.go b/daemon/algod/api/server/common/metrics.go
index 4920acfb6..18e6b8027 100644
--- a/daemon/algod/api/server/common/metrics.go
+++ b/daemon/algod/api/server/common/metrics.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/common/responses.go b/daemon/algod/api/server/common/responses.go
index 452ed5808..b775d2040 100644
--- a/daemon/algod/api/server/common/responses.go
+++ b/daemon/algod/api/server/common/responses.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/common/routes.go b/daemon/algod/api/server/common/routes.go
index 153a685bf..545d92bb2 100644
--- a/daemon/algod/api/server/common/routes.go
+++ b/daemon/algod/api/server/common/routes.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/lib/common.go b/daemon/algod/api/server/lib/common.go
index 0bc739484..2599774a8 100644
--- a/daemon/algod/api/server/lib/common.go
+++ b/daemon/algod/api/server/lib/common.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/lib/middlewares/auth.go b/daemon/algod/api/server/lib/middlewares/auth.go
index 168afdc84..363e2b2b0 100644
--- a/daemon/algod/api/server/lib/middlewares/auth.go
+++ b/daemon/algod/api/server/lib/middlewares/auth.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/lib/middlewares/auth_test.go b/daemon/algod/api/server/lib/middlewares/auth_test.go
index 7e00b66c3..4bdfa7b85 100644
--- a/daemon/algod/api/server/lib/middlewares/auth_test.go
+++ b/daemon/algod/api/server/lib/middlewares/auth_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/lib/middlewares/connectionLimiter.go b/daemon/algod/api/server/lib/middlewares/connectionLimiter.go
index bf27ef2b6..21e6c0bd1 100644
--- a/daemon/algod/api/server/lib/middlewares/connectionLimiter.go
+++ b/daemon/algod/api/server/lib/middlewares/connectionLimiter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/lib/middlewares/connectionLimiter_test.go b/daemon/algod/api/server/lib/middlewares/connectionLimiter_test.go
index 6de0a3c20..b3e5ea06a 100644
--- a/daemon/algod/api/server/lib/middlewares/connectionLimiter_test.go
+++ b/daemon/algod/api/server/lib/middlewares/connectionLimiter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/lib/middlewares/cors.go b/daemon/algod/api/server/lib/middlewares/cors.go
index 71cacb280..4bdf532e3 100644
--- a/daemon/algod/api/server/lib/middlewares/cors.go
+++ b/daemon/algod/api/server/lib/middlewares/cors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/lib/middlewares/logger.go b/daemon/algod/api/server/lib/middlewares/logger.go
index 94559abd0..5ede3aa49 100644
--- a/daemon/algod/api/server/lib/middlewares/logger.go
+++ b/daemon/algod/api/server/lib/middlewares/logger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/router.go b/daemon/algod/api/server/router.go
index 9f6c4277b..ab79ee305 100644
--- a/daemon/algod/api/server/router.go
+++ b/daemon/algod/api/server/router.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -30,6 +30,7 @@ import (
"github.com/algorand/go-algorand/daemon/algod/api/server/lib/middlewares"
"github.com/algorand/go-algorand/daemon/algod/api/server/v1/routes"
v2 "github.com/algorand/go-algorand/daemon/algod/api/server/v2"
+ "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/experimental"
npprivate "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/nonparticipating/private"
nppublic "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/nonparticipating/public"
pprivate "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/participating/private"
@@ -112,6 +113,10 @@ func NewRouter(logger logging.Logger, node *node.AlgorandFullNode, shutdown <-ch
ppublic.RegisterHandlers(e, &v2Handler, apiAuthenticator)
pprivate.RegisterHandlers(e, &v2Handler, adminAuthenticator)
+ if node.Config().EnableExperimentalAPI {
+ experimental.RegisterHandlers(e, &v2Handler, apiAuthenticator)
+ }
+
return e
}
diff --git a/daemon/algod/api/server/router_test.go b/daemon/algod/api/server/router_test.go
index 0e481e5fc..98e772f27 100644
--- a/daemon/algod/api/server/router_test.go
+++ b/daemon/algod/api/server/router_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v1/handlers/errors.go b/daemon/algod/api/server/v1/handlers/errors.go
index 0506eea6e..09720c78e 100644
--- a/daemon/algod/api/server/v1/handlers/errors.go
+++ b/daemon/algod/api/server/v1/handlers/errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v1/handlers/handlers.go b/daemon/algod/api/server/v1/handlers/handlers.go
index e1209cf1d..f5b914687 100644
--- a/daemon/algod/api/server/v1/handlers/handlers.go
+++ b/daemon/algod/api/server/v1/handlers/handlers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v1/routes/routes.go b/daemon/algod/api/server/v1/routes/routes.go
index d5f1e3a4b..dc163454e 100644
--- a/daemon/algod/api/server/v1/routes/routes.go
+++ b/daemon/algod/api/server/v1/routes/routes.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v2/account.go b/daemon/algod/api/server/v2/account.go
index 4c2392c06..9c25021b9 100644
--- a/daemon/algod/api/server/v2/account.go
+++ b/daemon/algod/api/server/v2/account.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v2/account_test.go b/daemon/algod/api/server/v2/account_test.go
index c6562abee..ac1abd3b9 100644
--- a/daemon/algod/api/server/v2/account_test.go
+++ b/daemon/algod/api/server/v2/account_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v2/delta.go b/daemon/algod/api/server/v2/delta.go
index d97cc2d42..f48115a3f 100644
--- a/daemon/algod/api/server/v2/delta.go
+++ b/daemon/algod/api/server/v2/delta.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v2/delta_test.go b/daemon/algod/api/server/v2/delta_test.go
index 921798d28..596954f43 100644
--- a/daemon/algod/api/server/v2/delta_test.go
+++ b/daemon/algod/api/server/v2/delta_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v2/dryrun.go b/daemon/algod/api/server/v2/dryrun.go
index f11ad58ea..49a5a427d 100644
--- a/daemon/algod/api/server/v2/dryrun.go
+++ b/daemon/algod/api/server/v2/dryrun.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -181,24 +181,22 @@ func (ddr *dryrunDebugReceiver) stateToState(state *logic.DebugState) model.Dryr
return st
}
-// Register is fired on program creation (DebuggerHook interface)
-func (ddr *dryrunDebugReceiver) Register(state *logic.DebugState) error {
+// Register is fired on program creation (logic.Debugger interface)
+func (ddr *dryrunDebugReceiver) Register(state *logic.DebugState) {
ddr.disassembly = state.Disassembly
ddr.lines = strings.Split(state.Disassembly, "\n")
- return nil
}
-// Update is fired on every step (DebuggerHook interface)
-func (ddr *dryrunDebugReceiver) Update(state *logic.DebugState) error {
+// Update is fired on every step (logic.Debugger interface)
+func (ddr *dryrunDebugReceiver) Update(state *logic.DebugState) {
st := ddr.stateToState(state)
ddr.history = append(ddr.history, st)
ddr.updateScratch()
- return nil
}
-// Complete is called when the program exits (DebuggerHook interface)
-func (ddr *dryrunDebugReceiver) Complete(state *logic.DebugState) error {
- return ddr.Update(state)
+// Complete is called when the program exits (logic.Debugger interface)
+func (ddr *dryrunDebugReceiver) Complete(state *logic.DebugState) {
+ ddr.Update(state)
}
type dryrunLedger struct {
@@ -421,7 +419,7 @@ func doDryrunRequest(dr *DryrunRequest, response *model.DryrunResponse) {
var result model.DryrunTxnResult
if len(stxn.Lsig.Logic) > 0 {
var debug dryrunDebugReceiver
- ep.Debugger = &debug
+ ep.Tracer = logic.MakeEvalTracerDebuggerAdaptor(&debug)
ep.SigLedger = &dl
pass, err := logic.EvalSignature(ti, ep)
var messages []string
@@ -505,7 +503,7 @@ func doDryrunRequest(dr *DryrunRequest, response *model.DryrunResponse) {
messages[0] = fmt.Sprintf("uploaded state did not include app id %d referenced in txn[%d]", appIdx, ti)
} else {
var debug dryrunDebugReceiver
- ep.Debugger = &debug
+ ep.Tracer = logic.MakeEvalTracerDebuggerAdaptor(&debug)
var program []byte
messages = make([]string, 1)
if stxn.Txn.OnCompletion == transactions.ClearStateOC {
diff --git a/daemon/algod/api/server/v2/dryrun_test.go b/daemon/algod/api/server/v2/dryrun_test.go
index 9ec7444e5..fd74e9010 100644
--- a/daemon/algod/api/server/v2/dryrun_test.go
+++ b/daemon/algod/api/server/v2/dryrun_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v2/errors.go b/daemon/algod/api/server/v2/errors.go
index 5df5a920d..947a38fa9 100644
--- a/daemon/algod/api/server/v2/errors.go
+++ b/daemon/algod/api/server/v2/errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -29,14 +29,12 @@ var (
errFailedRetrievingLatestBlockHeaderStatus = "failed retrieving latests block header"
errFailedRetrievingSyncRound = "failed retrieving sync round from ledger"
errFailedSettingSyncRound = "failed to set sync round on the ledger"
- errSyncModeNotEnabled = "sync mode must be enabled"
errFailedParsingFormatOption = "failed to parse the format option"
errFailedToParseAddress = "failed to parse the address"
errFailedToParseExclude = "failed to parse exclude"
errFailedToParseTransaction = "failed to parse transaction"
errFailedToParseBlock = "failed to parse block"
errFailedToParseCert = "failed to parse cert"
- errFailedToParseSourcemap = "failed to parse sourcemap"
errFailedToEncodeResponse = "failed to encode response"
errInternalFailure = "internal failure"
errNoValidTxnSpecified = "no valid transaction ID was specified"
diff --git a/daemon/algod/api/server/v2/generated/data/data_routes.yml b/daemon/algod/api/server/v2/generated/data/data_routes.yml
index 6de9f2be5..06a630649 100644
--- a/daemon/algod/api/server/v2/generated/data/data_routes.yml
+++ b/daemon/algod/api/server/v2/generated/data/data_routes.yml
@@ -11,6 +11,7 @@ output-options:
- private
- participating
- nonparticipating
+ - experimental
type-mappings:
integer: uint64
skip-prune: true
diff --git a/daemon/algod/api/server/v2/generated/data/routes.go b/daemon/algod/api/server/v2/generated/data/routes.go
index 3e44f8c82..8b842d8bb 100644
--- a/daemon/algod/api/server/v2/generated/data/routes.go
+++ b/daemon/algod/api/server/v2/generated/data/routes.go
@@ -136,174 +136,178 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
- "jbnbH2//fwAAAP//SDOA3yjJAAA=",
+ "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",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/daemon/algod/api/server/v2/generated/experimental/experimental_routes.yml b/daemon/algod/api/server/v2/generated/experimental/experimental_routes.yml
new file mode 100644
index 000000000..d62923b4b
--- /dev/null
+++ b/daemon/algod/api/server/v2/generated/experimental/experimental_routes.yml
@@ -0,0 +1,21 @@
+package: experimental
+generate:
+ echo-server: true
+ embedded-spec: true
+output-options:
+ include-tags:
+ - experimental
+ - public
+ exclude-tags:
+ - common
+ - private
+ - participating
+ - nonparticipating
+ - data
+ type-mappings:
+ integer: uint64
+ skip-prune: true
+additional-imports:
+ - alias: "."
+ package: "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model"
+output: ./server/v2/generated/experimental/routes.go
diff --git a/daemon/algod/api/server/v2/generated/experimental/routes.go b/daemon/algod/api/server/v2/generated/experimental/routes.go
new file mode 100644
index 000000000..105fee622
--- /dev/null
+++ b/daemon/algod/api/server/v2/generated/experimental/routes.go
@@ -0,0 +1,321 @@
+// Package experimental provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/algorand/oapi-codegen DO NOT EDIT.
+package experimental
+
+import (
+ "bytes"
+ "compress/gzip"
+ "encoding/base64"
+ "fmt"
+ "net/url"
+ "path"
+ "strings"
+
+ . "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model"
+ "github.com/getkin/kin-openapi/openapi3"
+ "github.com/labstack/echo/v4"
+)
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+ // Simulates a raw transaction or transaction group as it would be evaluated on the network. WARNING: This endpoint is experimental and under active development. There are no guarantees in terms of functionality or future support.
+ // (POST /v2/transactions/simulate)
+ SimulateTransaction(ctx echo.Context) error
+}
+
+// ServerInterfaceWrapper converts echo contexts to parameters.
+type ServerInterfaceWrapper struct {
+ Handler ServerInterface
+}
+
+// SimulateTransaction converts echo context to params.
+func (w *ServerInterfaceWrapper) SimulateTransaction(ctx echo.Context) error {
+ var err error
+
+ ctx.Set(Api_keyScopes, []string{""})
+
+ // Invoke the callback with all the unmarshalled arguments
+ err = w.Handler.SimulateTransaction(ctx)
+ return err
+}
+
+// This is a simple interface which specifies echo.Route addition functions which
+// are present on both echo.Echo and echo.Group, since we want to allow using
+// either of them for path registration
+type EchoRouter interface {
+ CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+}
+
+// RegisterHandlers adds each server route to the EchoRouter.
+func RegisterHandlers(router EchoRouter, si ServerInterface, m ...echo.MiddlewareFunc) {
+ RegisterHandlersWithBaseURL(router, si, "", m...)
+}
+
+// Registers handlers, and prepends BaseURL to the paths, so that the paths
+// can be served under a prefix.
+func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string, m ...echo.MiddlewareFunc) {
+
+ wrapper := ServerInterfaceWrapper{
+ Handler: si,
+ }
+
+ router.POST(baseURL+"/v2/transactions/simulate", wrapper.SimulateTransaction, m...)
+
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+ "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",
+}
+
+// GetSwagger returns the content of the embedded swagger specification file
+// or error if failed to decode
+func decodeSpec() ([]byte, error) {
+ zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
+ if err != nil {
+ return nil, fmt.Errorf("error base64 decoding spec: %s", err)
+ }
+ zr, err := gzip.NewReader(bytes.NewReader(zipped))
+ if err != nil {
+ return nil, fmt.Errorf("error decompressing spec: %s", err)
+ }
+ var buf bytes.Buffer
+ _, err = buf.ReadFrom(zr)
+ if err != nil {
+ return nil, fmt.Errorf("error decompressing spec: %s", err)
+ }
+
+ return buf.Bytes(), nil
+}
+
+var rawSpec = decodeSpecCached()
+
+// a naive cached of a decoded swagger spec
+func decodeSpecCached() func() ([]byte, error) {
+ data, err := decodeSpec()
+ return func() ([]byte, error) {
+ return data, err
+ }
+}
+
+// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
+func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
+ var res = make(map[string]func() ([]byte, error))
+ if len(pathToFile) > 0 {
+ res[pathToFile] = rawSpec
+ }
+
+ return res
+}
+
+// GetSwagger returns the Swagger specification corresponding to the generated code
+// in this file. The external references of Swagger specification are resolved.
+// The logic of resolving external references is tightly connected to "import-mapping" feature.
+// Externally referenced files must be embedded in the corresponding golang packages.
+// Urls can be supported but this task was out of the scope.
+func GetSwagger() (swagger *openapi3.T, err error) {
+ var resolvePath = PathToRawSpec("")
+
+ loader := openapi3.NewLoader()
+ loader.IsExternalRefsAllowed = true
+ loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
+ var pathToFile = url.String()
+ pathToFile = path.Clean(pathToFile)
+ getSpec, ok := resolvePath[pathToFile]
+ if !ok {
+ err1 := fmt.Errorf("path not found: %s", pathToFile)
+ return nil, err1
+ }
+ return getSpec()
+ }
+ var specData []byte
+ specData, err = rawSpec()
+ if err != nil {
+ return
+ }
+ swagger, err = loader.LoadFromData(specData)
+ if err != nil {
+ return
+ }
+ return
+}
diff --git a/daemon/algod/api/server/v2/generated/model/types.go b/daemon/algod/api/server/v2/generated/model/types.go
index f184d1a5f..e9e07b359 100644
--- a/daemon/algod/api/server/v2/generated/model/types.go
+++ b/daemon/algod/api/server/v2/generated/model/types.go
@@ -1045,6 +1045,30 @@ type NodeStatusResponse struct {
// TimeSinceLastRound TimeSinceLastRound in nanoseconds
TimeSinceLastRound uint64 `json:"time-since-last-round"`
+
+ // UpgradeDelay Upgrade delay
+ UpgradeDelay *uint64 `json:"upgrade-delay,omitempty"`
+
+ // UpgradeNextProtocolVoteBefore Next protocol round
+ UpgradeNextProtocolVoteBefore *uint64 `json:"upgrade-next-protocol-vote-before,omitempty"`
+
+ // UpgradeNoVotes No votes cast for consensus upgrade
+ UpgradeNoVotes *uint64 `json:"upgrade-no-votes,omitempty"`
+
+ // UpgradeNodeVote This node's upgrade vote
+ UpgradeNodeVote *bool `json:"upgrade-node-vote,omitempty"`
+
+ // UpgradeVoteRounds Total voting ounds for current upgrade
+ UpgradeVoteRounds *uint64 `json:"upgrade-vote-rounds,omitempty"`
+
+ // UpgradeVotes Total votes cast for consensus upgrade
+ UpgradeVotes *uint64 `json:"upgrade-votes,omitempty"`
+
+ // UpgradeVotesRequired Yes votes required for consensus upgrade
+ UpgradeVotesRequired *uint64 `json:"upgrade-votes-required,omitempty"`
+
+ // UpgradeYesVotes Yes votes cast for consensus upgrade
+ UpgradeYesVotes *uint64 `json:"upgrade-yes-votes,omitempty"`
}
// ParticipationKeyResponse Represents a participation key used by the node.
@@ -1074,6 +1098,15 @@ type PostTransactionsResponse struct {
TxId string `json:"txId"`
}
+// SimulationResponse defines model for SimulationResponse.
+type SimulationResponse struct {
+ // FailureMessage \[fm\] Failure message, if the transaction would have failed during a live broadcast.
+ FailureMessage string `json:"failure-message"`
+
+ // MissingSignatures \[ms\] Whether any transactions would have failed during a live broadcast because they were missing signatures.
+ MissingSignatures bool `json:"missing-signatures"`
+}
+
// StateProofResponse Represents a state proof and its corresponding message
type StateProofResponse = StateProof
diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go
index 64f2bc284..bc7cdf5d5 100644
--- a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go
+++ b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go
@@ -130,173 +130,177 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
- "waR6124+3fz/AAAA///m3Czx7MQAAA==",
+ "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",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/public/public_routes.yml b/daemon/algod/api/server/v2/generated/nonparticipating/public/public_routes.yml
index 0cc18f190..8566bf764 100644
--- a/daemon/algod/api/server/v2/generated/nonparticipating/public/public_routes.yml
+++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/public_routes.yml
@@ -11,6 +11,7 @@ output-options:
- common
- participating
- data
+ - experimental
type-mappings:
integer: uint64
skip-prune: true
diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go
index 3ec9dfd56..dbfb7a17c 100644
--- a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go
+++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go
@@ -63,7 +63,7 @@ type ServerInterface interface {
// Gets the current node status.
// (GET /v2/status)
GetStatus(ctx echo.Context) error
- // Gets the node status after waiting for the given round.
+ // Gets the node status after waiting for a round after the given round.
// (GET /v2/status/wait-for-block-after/{round})
WaitForBlock(ctx echo.Context, round uint64) error
// Compile TEAL source code to binary, produce its hash
@@ -548,227 +548,234 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
+ "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",
+ "qt9//P8BAAD//0vKaWunRwEA",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/daemon/algod/api/server/v2/generated/participating/private/routes.go b/daemon/algod/api/server/v2/generated/participating/private/routes.go
index 6b1676e8e..9052808c0 100644
--- a/daemon/algod/api/server/v2/generated/participating/private/routes.go
+++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go
@@ -158,175 +158,179 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
+ "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",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/daemon/algod/api/server/v2/generated/participating/public/public_routes.yml b/daemon/algod/api/server/v2/generated/participating/public/public_routes.yml
index 5dac08d5b..a568d0544 100644
--- a/daemon/algod/api/server/v2/generated/participating/public/public_routes.yml
+++ b/daemon/algod/api/server/v2/generated/participating/public/public_routes.yml
@@ -11,6 +11,7 @@ output-options:
- common
- nonparticipating
- data
+ - experimental
type-mappings:
integer: uint64
skip-prune: true
diff --git a/daemon/algod/api/server/v2/generated/participating/public/routes.go b/daemon/algod/api/server/v2/generated/participating/public/routes.go
index 613a5d1cd..bf14f4ef1 100644
--- a/daemon/algod/api/server/v2/generated/participating/public/routes.go
+++ b/daemon/algod/api/server/v2/generated/participating/public/routes.go
@@ -24,7 +24,7 @@ type ServerInterface interface {
// Get a list of unconfirmed transactions currently in the transaction pool by address.
// (GET /v2/accounts/{address}/transactions/pending)
GetPendingTransactionsByAddress(ctx echo.Context, address string, params GetPendingTransactionsByAddressParams) error
- // Broadcasts a raw transaction to the network.
+ // Broadcasts a raw transaction or transaction group to the network.
// (POST /v2/transactions)
RawTransaction(ctx echo.Context) error
// Get a list of unconfirmed transactions currently in the transaction pool.
@@ -177,183 +177,188 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
+ "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",
+ "XP+/AAAA//+u3heREOIAAA==",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go
index 60a0a8e5d..329902152 100644
--- a/daemon/algod/api/server/v2/handlers.go
+++ b/daemon/algod/api/server/v2/handlers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -30,6 +30,7 @@ import (
"github.com/labstack/echo/v4"
+ "github.com/algorand/avm-abi/apps"
"github.com/algorand/go-codec/codec"
"github.com/algorand/go-algorand/agreement"
@@ -45,6 +46,7 @@ import (
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
"github.com/algorand/go-algorand/ledger/ledgercore"
+ "github.com/algorand/go-algorand/ledger/simulation"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/node"
"github.com/algorand/go-algorand/protocol"
@@ -96,6 +98,7 @@ type NodeInterface interface {
GenesisID() string
GenesisHash() crypto.Digest
BroadcastSignedTxGroup(txgroup []transactions.SignedTxn) error
+ Simulate(txgroup []transactions.SignedTxn) (vb *ledgercore.ValidatedBlock, missingSignatures bool, err error)
GetPendingTransaction(txID transactions.Txid) (res node.TxnWithStatus, found bool)
GetPendingTxnsFromPool() ([]transactions.SignedTxn, error)
SuggestedFee() basics.MicroAlgos
@@ -790,6 +793,29 @@ func (v2 *Handlers) GetStatus(ctx echo.Context) error {
CatchpointAcquiredBlocks: &stat.CatchpointCatchupAcquiredBlocks,
}
+ nextProtocolVoteBefore := uint64(stat.NextProtocolVoteBefore)
+ var votesToGo int64 = int64(nextProtocolVoteBefore) - int64(stat.LastRound)
+ if votesToGo < 0 {
+ votesToGo = 0
+ }
+ if nextProtocolVoteBefore > 0 {
+ consensus := config.Consensus[protocol.ConsensusCurrentVersion]
+ upgradeVoteRounds := consensus.UpgradeVoteRounds
+ upgradeThreshold := consensus.UpgradeThreshold
+ votes := uint64(consensus.UpgradeVoteRounds) - uint64(votesToGo)
+ votesYes := stat.NextProtocolApprovals
+ votesNo := votes - votesYes
+ upgradeDelay := uint64(stat.UpgradeDelay)
+ response.UpgradeVotesRequired = &upgradeThreshold
+ response.UpgradeNodeVote = &stat.UpgradeApprove
+ response.UpgradeDelay = &upgradeDelay
+ response.UpgradeVotes = &votes
+ response.UpgradeYesVotes = &votesYes
+ response.UpgradeNoVotes = &votesNo
+ response.UpgradeNextProtocolVoteBefore = &nextProtocolVoteBefore
+ response.UpgradeVoteRounds = &upgradeVoteRounds
+ }
+
return ctx.JSON(http.StatusOK, response)
}
@@ -836,21 +862,10 @@ func (v2 *Handlers) WaitForBlock(ctx echo.Context, round uint64) error {
return v2.GetStatus(ctx)
}
-// RawTransaction broadcasts a raw transaction to the network.
-// (POST /v2/transactions)
-func (v2 *Handlers) RawTransaction(ctx echo.Context) error {
- stat, err := v2.Node.Status()
- if err != nil {
- return internalError(ctx, err, errFailedRetrievingNodeStatus, v2.Log)
- }
- if stat.Catchpoint != "" {
- // node is currently catching up to the requested catchpoint.
- return serviceUnavailable(ctx, fmt.Errorf("RawTransaction failed as the node was catchpoint catchuping"), errOperationNotAvailableDuringCatchup, v2.Log)
- }
- proto := config.Consensus[stat.LastVersion]
-
+// decodeTxGroup attempts to decode a request body containing a transaction group.
+func decodeTxGroup(body io.Reader, maxTxGroupSize int) ([]transactions.SignedTxn, error) {
var txgroup []transactions.SignedTxn
- dec := protocol.NewDecoder(ctx.Request().Body)
+ dec := protocol.NewDecoder(body)
for {
var st transactions.SignedTxn
err := dec.Decode(&st)
@@ -858,18 +873,38 @@ func (v2 *Handlers) RawTransaction(ctx echo.Context) error {
break
}
if err != nil {
- return badRequest(ctx, err, err.Error(), v2.Log)
+ return nil, err
}
txgroup = append(txgroup, st)
- if len(txgroup) > proto.MaxTxGroupSize {
- err := fmt.Errorf("max group size is %d", proto.MaxTxGroupSize)
- return badRequest(ctx, err, err.Error(), v2.Log)
+ if len(txgroup) > maxTxGroupSize {
+ err := fmt.Errorf("max group size is %d", maxTxGroupSize)
+ return nil, err
}
}
if len(txgroup) == 0 {
- err := errors.New("empty txgroup")
+ return nil, errors.New("empty txgroup")
+ }
+
+ return txgroup, nil
+}
+
+// RawTransaction broadcasts a raw transaction to the network.
+// (POST /v2/transactions)
+func (v2 *Handlers) RawTransaction(ctx echo.Context) error {
+ stat, err := v2.Node.Status()
+ if err != nil {
+ return internalError(ctx, err, errFailedRetrievingNodeStatus, v2.Log)
+ }
+ if stat.Catchpoint != "" {
+ // node is currently catching up to the requested catchpoint.
+ return serviceUnavailable(ctx, fmt.Errorf("RawTransaction failed as the node was catchpoint catchuping"), errOperationNotAvailableDuringCatchup, v2.Log)
+ }
+ proto := config.Consensus[stat.LastVersion]
+
+ txgroup, err := decodeTxGroup(ctx.Request().Body, proto.MaxTxGroupSize)
+ if err != nil {
return badRequest(ctx, err, err.Error(), v2.Log)
}
@@ -883,6 +918,57 @@ func (v2 *Handlers) RawTransaction(ctx echo.Context) error {
return ctx.JSON(http.StatusOK, model.PostTransactionsResponse{TxId: txid.String()})
}
+// SimulateTransaction simulates broadcasting a raw transaction to the network, returning relevant simulation results.
+// (POST /v2/transactions/simulate)
+func (v2 *Handlers) SimulateTransaction(ctx echo.Context) error {
+ if !v2.Node.Config().EnableExperimentalAPI {
+ // Right now this is a redundant/useless check at runtime, since experimental APIs are not registered when EnableExperimentalAPI=false.
+ // However, this endpoint won't always be experimental, so I've left this here as a reminder to have some other flag guarding its usage.
+ return ctx.String(http.StatusNotFound, fmt.Sprintf("%s was not enabled in the configuration file by setting EnableExperimentalAPI to true", ctx.Request().URL.Path))
+ }
+
+ stat, err := v2.Node.Status()
+ if err != nil {
+ return internalError(ctx, err, errFailedRetrievingNodeStatus, v2.Log)
+ }
+ if stat.Catchpoint != "" {
+ // node is currently catching up to the requested catchpoint.
+ return serviceUnavailable(ctx, fmt.Errorf("SimulateTransaction failed as the node was catchpoint catchuping"), errOperationNotAvailableDuringCatchup, v2.Log)
+ }
+ proto := config.Consensus[stat.LastVersion]
+
+ txgroup, err := decodeTxGroup(ctx.Request().Body, proto.MaxTxGroupSize)
+ if err != nil {
+ return badRequest(ctx, err, err.Error(), v2.Log)
+ }
+
+ var res model.SimulationResponse
+
+ // Simulate transaction
+ _, missingSignatures, err := v2.Node.Simulate(txgroup)
+ if err != nil {
+ var invalidTxErr *simulation.InvalidTxGroupError
+ var evalErr *simulation.EvalFailureError
+ switch {
+ case errors.As(err, &invalidTxErr):
+ return badRequest(ctx, invalidTxErr, invalidTxErr.Error(), v2.Log)
+ case errors.As(err, &evalErr):
+ res.FailureMessage = evalErr.Error()
+ default:
+ return internalError(ctx, err, err.Error(), v2.Log)
+ }
+ }
+ res.MissingSignatures = missingSignatures
+
+ // Return msgpack response
+ msgpack, err := encode(protocol.CodecHandle, &res)
+ if err != nil {
+ return internalError(ctx, err, errFailedToEncodeResponse, v2.Log)
+ }
+
+ return ctx.Blob(http.StatusOK, "application/msgpack", msgpack)
+}
+
// TealDryrun takes transactions and additional simulated ledger state and returns debugging information.
// (POST /v2/teal/dryrun)
func (v2 *Handlers) TealDryrun(ctx echo.Context) error {
@@ -1291,7 +1377,7 @@ func (v2 *Handlers) GetApplicationBoxes(ctx echo.Context, applicationID uint64,
appIdx := basics.AppIndex(applicationID)
ledger := v2.Node.LedgerForAPI()
lastRound := ledger.Latest()
- keyPrefix := logic.MakeBoxKey(appIdx, "")
+ keyPrefix := apps.MakeBoxKey(uint64(appIdx), "")
requestedMax, algodMax := nilToZero(params.Max), v2.Node.Config().MaxAPIBoxPerApplication
max := applicationBoxesMaxKeys(requestedMax, algodMax)
@@ -1337,7 +1423,7 @@ func (v2 *Handlers) GetApplicationBoxByName(ctx echo.Context, applicationID uint
lastRound := ledger.Latest()
encodedBoxName := params.Name
- boxNameBytes, err := logic.NewAppCallBytes(encodedBoxName)
+ boxNameBytes, err := apps.NewAppCallBytes(encodedBoxName)
if err != nil {
return badRequest(ctx, err, err.Error(), v2.Log)
}
@@ -1346,7 +1432,7 @@ func (v2 *Handlers) GetApplicationBoxByName(ctx echo.Context, applicationID uint
return badRequest(ctx, err, err.Error(), v2.Log)
}
- value, err := ledger.LookupKv(lastRound, logic.MakeBoxKey(appIdx, string(boxName)))
+ value, err := ledger.LookupKv(lastRound, apps.MakeBoxKey(uint64(appIdx), string(boxName)))
if err != nil {
return internalError(ctx, err, errFailedLookingUpLedger, v2.Log)
}
@@ -1437,7 +1523,7 @@ func (v2 *Handlers) TealCompile(ctx echo.Context, params model.TealCompileParams
ops, err := logic.AssembleString(source)
if err != nil {
sb := strings.Builder{}
- ops.ReportProblems("", &sb)
+ ops.ReportMultipleErrors("", &sb)
return badRequest(ctx, err, sb.String(), v2.Log)
}
pd := logic.HashProgram(ops.Program)
diff --git a/daemon/algod/api/server/v2/handlers_test.go b/daemon/algod/api/server/v2/handlers_test.go
index 98635897d..2a703e255 100644
--- a/daemon/algod/api/server/v2/handlers_test.go
+++ b/daemon/algod/api/server/v2/handlers_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/server/v2/test/handlers_resources_test.go b/daemon/algod/api/server/v2/test/handlers_resources_test.go
index d9ab3832a..04e36a3e4 100644
--- a/daemon/algod/api/server/v2/test/handlers_resources_test.go
+++ b/daemon/algod/api/server/v2/test/handlers_resources_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -225,7 +225,7 @@ func setupTestForLargeResources(t *testing.T, acctSize, maxResults int, accountM
acctData = accountMaker(acctSize)
ml.accounts[fakeAddr] = acctData
- mockNode := makeMockNode(&ml, t.Name(), nil)
+ mockNode := makeMockNode(&ml, t.Name(), nil, false)
mockNode.config.MaxAPIResourcesPerAccount = uint64(maxResults)
dummyShutdownChan := make(chan struct{})
handlers = v2.Handlers{
diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go
index 437dca2a3..dedd902c9 100644
--- a/daemon/algod/api/server/v2/test/handlers_test.go
+++ b/daemon/algod/api/server/v2/test/handlers_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -37,6 +37,7 @@ import (
"github.com/algorand/go-algorand/agreement"
"github.com/algorand/go-algorand/catchup"
+ "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"
@@ -59,12 +60,12 @@ import (
const stateProofIntervalForHandlerTests = uint64(256)
-func setupTestForMethodGet(t *testing.T) (v2.Handlers, echo.Context, *httptest.ResponseRecorder, []account.Root, []transactions.SignedTxn, func()) {
+func setupTestForMethodGet(t *testing.T, consensusUpgrade bool) (v2.Handlers, echo.Context, *httptest.ResponseRecorder, []account.Root, []transactions.SignedTxn, func()) {
numAccounts := 1
numTransactions := 1
offlineAccounts := true
mockLedger, rootkeys, _, stxns, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts)
- mockNode := makeMockNode(mockLedger, t.Name(), nil)
+ mockNode := makeMockNode(mockLedger, t.Name(), nil, consensusUpgrade)
dummyShutdownChan := make(chan struct{})
handler := v2.Handlers{
Node: mockNode,
@@ -82,13 +83,13 @@ func TestSimpleMockBuilding(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- handler, _, _, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, _, _, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
require.Equal(t, t.Name(), handler.Node.GenesisID())
}
func accountInformationTest(t *testing.T, address string, expectedCode int) {
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
err := handler.AccountInformation(c, address, model.AccountInformationParams{})
require.NoError(t, err)
@@ -111,7 +112,7 @@ func TestAccountInformation(t *testing.T) {
}
func getBlockTest(t *testing.T, blockNum uint64, format string, expectedCode int) {
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
err := handler.GetBlock(c, blockNum, model.GetBlockParams{Format: (*model.GetBlockParamsFormat)(&format)})
require.NoError(t, err)
@@ -134,7 +135,7 @@ func TestGetLedgerStateDelta(t *testing.T) {
t.Parallel()
a := require.New(t)
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
insertRounds(a, handler, 3)
@@ -163,7 +164,7 @@ func TestSyncRound(t *testing.T) {
numTransactions := 1
offlineAccounts := true
mockLedger, _, _, _, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts)
- mockNode := makeMockNode(mockLedger, t.Name(), nil)
+ mockNode := makeMockNode(mockLedger, t.Name(), nil, false)
dummyShutdownChan := make(chan struct{})
handler := v2.Handlers{
Node: mockNode,
@@ -224,7 +225,7 @@ func TestSyncRound(t *testing.T) {
}
func addBlockHelper(t *testing.T) (v2.Handlers, echo.Context, *httptest.ResponseRecorder, transactions.SignedTxn, func()) {
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
l := handler.Node.LedgerForAPI()
@@ -308,7 +309,7 @@ func TestGetBlockHash(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
err := handler.GetBlockHash(c, 0)
@@ -326,7 +327,7 @@ func TestGetBlockGetBlockHash(t *testing.T) {
t.Parallel()
a := require.New(t)
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
insertRounds(a, handler, 2)
@@ -398,7 +399,7 @@ func TestGetSupply(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- handler, c, _, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, _, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
err := handler.GetSupply(c)
require.NoError(t, err)
@@ -408,7 +409,7 @@ func TestGetStatus(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
err := handler.GetStatus(c)
require.NoError(t, err)
@@ -439,11 +440,60 @@ func TestGetStatus(t *testing.T) {
require.Equal(t, expectedResult, actualResult)
}
+func TestGetStatusConsensusUpgrade(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, true)
+ defer releasefunc()
+ err := handler.GetStatus(c)
+ require.NoError(t, err)
+ stat := cannedStatusReportConsensusUpgradeGolden
+ consensus := config.Consensus[protocol.ConsensusCurrentVersion]
+ votesToGo := uint64(stat.NextProtocolVoteBefore) - uint64(stat.LastRound)
+ nextProtocolVoteBefore := uint64(stat.NextProtocolVoteBefore)
+ votes := uint64(consensus.UpgradeVoteRounds) - votesToGo
+ votesNo := votes - stat.NextProtocolApprovals
+
+ expectedResult := model.NodeStatusResponse{
+ LastRound: uint64(stat.LastRound),
+ LastVersion: string(stat.LastVersion),
+ NextVersion: string(stat.NextVersion),
+ NextVersionRound: uint64(stat.NextVersionRound),
+ NextVersionSupported: stat.NextVersionSupported,
+ TimeSinceLastRound: uint64(stat.TimeSinceLastRound().Nanoseconds()),
+ CatchupTime: uint64(stat.CatchupTime.Nanoseconds()),
+ StoppedAtUnsupportedRound: stat.StoppedAtUnsupportedRound,
+ LastCatchpoint: &stat.LastCatchpoint,
+ Catchpoint: &stat.Catchpoint,
+ CatchpointTotalAccounts: &stat.CatchpointCatchupTotalAccounts,
+ CatchpointProcessedAccounts: &stat.CatchpointCatchupProcessedAccounts,
+ CatchpointVerifiedAccounts: &stat.CatchpointCatchupVerifiedAccounts,
+ CatchpointTotalBlocks: &stat.CatchpointCatchupTotalBlocks,
+ CatchpointAcquiredBlocks: &stat.CatchpointCatchupAcquiredBlocks,
+ CatchpointTotalKvs: &stat.CatchpointCatchupTotalKVs,
+ CatchpointProcessedKvs: &stat.CatchpointCatchupProcessedKVs,
+ CatchpointVerifiedKvs: &stat.CatchpointCatchupVerifiedKVs,
+ UpgradeVotesRequired: &consensus.UpgradeThreshold,
+ UpgradeNodeVote: &stat.UpgradeApprove,
+ UpgradeDelay: &stat.UpgradeDelay,
+ UpgradeNoVotes: &votesNo,
+ UpgradeYesVotes: &stat.NextProtocolApprovals,
+ UpgradeVoteRounds: &consensus.UpgradeVoteRounds,
+ UpgradeNextProtocolVoteBefore: &nextProtocolVoteBefore,
+ UpgradeVotes: &votes,
+ }
+ actualResult := model.NodeStatusResponse{}
+ err = protocol.DecodeJSON(rec.Body.Bytes(), &actualResult)
+ require.NoError(t, err)
+ require.Equal(t, expectedResult, actualResult)
+}
+
func TestGetStatusAfterBlock(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
err := handler.WaitForBlock(c, 0)
require.NoError(t, err)
@@ -456,7 +506,7 @@ func TestGetTransactionParams(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
err := handler.TransactionParams(c)
require.NoError(t, err)
@@ -464,7 +514,7 @@ func TestGetTransactionParams(t *testing.T) {
}
func pendingTransactionInformationTest(t *testing.T, txidToUse int, format string, expectedCode int) {
- handler, c, rec, _, stxns, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, stxns, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
txid := "bad txid"
if txidToUse >= 0 {
@@ -487,7 +537,7 @@ func TestPendingTransactionInformation(t *testing.T) {
}
func getPendingTransactionsTest(t *testing.T, format string, max uint64, expectedCode int) {
- handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
params := model.GetPendingTransactionsParams{Format: (*model.GetPendingTransactionsParamsFormat)(&format), Max: &max}
err := handler.GetPendingTransactions(c, params)
@@ -566,7 +616,7 @@ func TestPendingTransactions(t *testing.T) {
}
func pendingTransactionsByAddressTest(t *testing.T, rootkeyToUse int, format string, expectedCode int) {
- handler, c, rec, rootkeys, _, releasefunc := setupTestForMethodGet(t)
+ handler, c, rec, rootkeys, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
address := "bad address"
if rootkeyToUse >= 0 {
@@ -588,15 +638,16 @@ func TestPendingTransactionsByAddress(t *testing.T) {
pendingTransactionsByAddressTest(t, -1, "json", 400)
}
-func postTransactionTest(t *testing.T, txnToUse, expectedCode int) {
+func prepareTransactionTest(t *testing.T, txnToUse, expectedCode int, enableTransactionSimulator bool) (handler v2.Handlers, c echo.Context, rec *httptest.ResponseRecorder, releasefunc func()) {
numAccounts := 5
numTransactions := 5
offlineAccounts := true
mockLedger, _, _, stxns, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts)
- defer releasefunc()
dummyShutdownChan := make(chan struct{})
- mockNode := makeMockNode(mockLedger, t.Name(), nil)
- handler := v2.Handlers{
+ mockNode := makeMockNode(mockLedger, t.Name(), nil, false)
+ mockNode.config.EnableExperimentalAPI = enableTransactionSimulator
+ handler = v2.Handlers{
+
Node: mockNode,
Log: logging.Base(),
Shutdown: dummyShutdownChan,
@@ -609,8 +660,14 @@ func postTransactionTest(t *testing.T, txnToUse, expectedCode int) {
body = bytes.NewReader(bodyBytes)
}
req := httptest.NewRequest(http.MethodPost, "/", body)
- rec := httptest.NewRecorder()
- c := e.NewContext(req, rec)
+ rec = httptest.NewRecorder()
+ c = e.NewContext(req, rec)
+ return
+}
+
+func postTransactionTest(t *testing.T, txnToUse, expectedCode int) {
+ handler, c, rec, releasefunc := prepareTransactionTest(t, txnToUse, expectedCode, false)
+ defer releasefunc()
err := handler.RawTransaction(c)
require.NoError(t, err)
require.Equal(t, expectedCode, rec.Code)
@@ -624,6 +681,23 @@ func TestPostTransaction(t *testing.T) {
postTransactionTest(t, 0, 200)
}
+func simulateTransactionTest(t *testing.T, txnToUse, expectedCode int, enableTransactionSimulator bool) {
+ handler, c, rec, releasefunc := prepareTransactionTest(t, txnToUse, expectedCode, enableTransactionSimulator)
+ defer releasefunc()
+ err := handler.SimulateTransaction(c)
+ require.NoError(t, err)
+ require.Equal(t, expectedCode, rec.Code)
+}
+
+func TestSimulateTransaction(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ simulateTransactionTest(t, -1, 400, true)
+ simulateTransactionTest(t, 0, 404, false)
+ simulateTransactionTest(t, 0, 200, true)
+}
+
func startCatchupTest(t *testing.T, catchpoint string, nodeError error, expectedCode int) {
numAccounts := 1
numTransactions := 1
@@ -631,7 +705,7 @@ func startCatchupTest(t *testing.T, catchpoint string, nodeError error, expected
mockLedger, _, _, _, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts)
defer releasefunc()
dummyShutdownChan := make(chan struct{})
- mockNode := makeMockNode(mockLedger, t.Name(), nodeError)
+ mockNode := makeMockNode(mockLedger, t.Name(), nodeError, false)
handler := v2.Handlers{
Node: mockNode,
Log: logging.Base(),
@@ -672,7 +746,7 @@ func abortCatchupTest(t *testing.T, catchpoint string, expectedCode int) {
mockLedger, _, _, _, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts)
defer releasefunc()
dummyShutdownChan := make(chan struct{})
- mockNode := makeMockNode(mockLedger, t.Name(), nil)
+ mockNode := makeMockNode(mockLedger, t.Name(), nil, false)
handler := v2.Handlers{
Node: mockNode,
Log: logging.Base(),
@@ -707,7 +781,7 @@ func tealCompileTest(t *testing.T, bytesToUse []byte, expectedCode int,
mockLedger, _, _, _, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts)
defer releasefunc()
dummyShutdownChan := make(chan struct{})
- mockNode := makeMockNode(mockLedger, t.Name(), nil)
+ mockNode := makeMockNode(mockLedger, t.Name(), nil, false)
mockNode.config.EnableDeveloperAPI = enableDeveloperAPI
handler := v2.Handlers{
Node: mockNode,
@@ -741,7 +815,7 @@ func TestTealCompile(t *testing.T) {
t.Parallel()
params := model.TealCompileParams{}
- tealCompileTest(t, nil, 200, true, params, nil) // nil program should work
+ tealCompileTest(t, nil, 400, true, params, nil) // nil program should NOT work
goodProgram := fmt.Sprintf(`#pragma version %d
int 1
@@ -778,7 +852,7 @@ func tealDisassembleTest(t *testing.T, program []byte, expectedCode int,
mockLedger, _, _, _, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts)
defer releasefunc()
dummyShutdownChan := make(chan struct{})
- mockNode := makeMockNode(mockLedger, t.Name(), nil)
+ mockNode := makeMockNode(mockLedger, t.Name(), nil, false)
mockNode.config.EnableDeveloperAPI = enableDeveloperAPI
handler := v2.Handlers{
Node: mockNode,
@@ -841,7 +915,7 @@ func tealDryrunTest(
mockLedger, _, _, _, releasefunc := testingenv(t, numAccounts, numTransactions, offlineAccounts)
defer releasefunc()
dummyShutdownChan := make(chan struct{})
- mockNode := makeMockNode(mockLedger, t.Name(), nil)
+ mockNode := makeMockNode(mockLedger, t.Name(), nil, false)
mockNode.config.EnableDeveloperAPI = enableDeveloperAPI
handler := v2.Handlers{
Node: mockNode,
@@ -960,7 +1034,7 @@ func TestAppendParticipationKeys(t *testing.T) {
mockLedger, _, _, _, releasefunc := testingenv(t, 1, 1, true)
defer releasefunc()
- mockNode := makeMockNode(mockLedger, t.Name(), nil)
+ mockNode := makeMockNode(mockLedger, t.Name(), nil, false)
handler := v2.Handlers{
Node: mockNode,
Log: logging.Base(),
@@ -1044,7 +1118,7 @@ func TestAppendParticipationKeys(t *testing.T) {
t.Run("Internal error", func(t *testing.T) {
// Create mock node with an error.
expectedErr := errors.New("expected error")
- mockNode := makeMockNode(mockLedger, t.Name(), expectedErr)
+ mockNode := makeMockNode(mockLedger, t.Name(), expectedErr, false)
handler := v2.Handlers{
Node: mockNode,
Log: logging.Base(),
@@ -1210,7 +1284,7 @@ func TestStateProofNotFound(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
- handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
insertRounds(a, handler, 700)
@@ -1223,7 +1297,7 @@ func TestStateProofHigherRoundThanLatest(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
- handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
a.NoError(handler.GetStateProof(ctx, 2))
@@ -1234,7 +1308,7 @@ func TestStateProof200(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
- handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
insertRounds(a, handler, 1000)
@@ -1252,7 +1326,7 @@ func TestHeaderProofRoundTooHigh(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
- handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
a.NoError(handler.GetLightBlockHeaderProof(ctx, 2))
@@ -1263,7 +1337,7 @@ func TestHeaderProofStateProofNotFound(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
- handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
insertRounds(a, handler, 700)
@@ -1276,7 +1350,7 @@ func TestGetBlockProof200(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
- handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t)
+ handler, ctx, responseRecorder, _, _, releasefunc := setupTestForMethodGet(t, false)
defer releasefunc()
insertRounds(a, handler, 1000)
diff --git a/daemon/algod/api/server/v2/test/helpers.go b/daemon/algod/api/server/v2/test/helpers.go
index f18ebf86b..51467b65d 100644
--- a/daemon/algod/api/server/v2/test/helpers.go
+++ b/daemon/algod/api/server/v2/test/helpers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -32,6 +32,7 @@ import (
"github.com/algorand/go-algorand/data"
"github.com/algorand/go-algorand/data/account"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
@@ -62,6 +63,30 @@ var cannedStatusReportGolden = node.StatusReport{
LastCatchpoint: "",
}
+var cannedStatusReportConsensusUpgradeGolden = node.StatusReport{
+ LastRound: basics.Round(97000),
+ LastVersion: protocol.ConsensusCurrentVersion,
+ NextVersion: protocol.ConsensusCurrentVersion,
+ NextVersionRound: 200000,
+ NextVersionSupported: true,
+ StoppedAtUnsupportedRound: true,
+ Catchpoint: "",
+ CatchpointCatchupAcquiredBlocks: 0,
+ CatchpointCatchupProcessedAccounts: 0,
+ CatchpointCatchupVerifiedAccounts: 0,
+ CatchpointCatchupTotalAccounts: 0,
+ CatchpointCatchupTotalKVs: 0,
+ CatchpointCatchupProcessedKVs: 0,
+ CatchpointCatchupVerifiedKVs: 0,
+ CatchpointCatchupTotalBlocks: 0,
+ LastCatchpoint: "",
+ UpgradePropose: "upgradePropose",
+ UpgradeApprove: false,
+ UpgradeDelay: 0,
+ NextProtocolVoteBefore: 100000,
+ NextProtocolApprovals: 5000,
+}
+
var poolAddrRewardBaseGolden = uint64(0)
var poolAddrAssetsGolden = make([]model.AssetHolding, 0)
var poolAddrCreatedAssetsGolden = make([]model.Asset, 0)
@@ -116,13 +141,14 @@ var poolDeltaResponseGolden = model.LedgerStateDelta{
// package `data` and package `node`, which themselves import `mocks`
type mockNode struct {
mock.Mock
- ledger v2.LedgerForAPI
- genesisID string
- config config.Local
- err error
- id account.ParticipationID
- keys account.StateProofKeys
- usertxns map[basics.Address][]node.TxnWithStatus
+ ledger v2.LedgerForAPI
+ genesisID string
+ config config.Local
+ err error
+ id account.ParticipationID
+ keys account.StateProofKeys
+ usertxns map[basics.Address][]node.TxnWithStatus
+ consensusUpgrade bool
}
func (m *mockNode) InstallParticipationKey(partKeyBinary []byte) (account.ParticipationID, error) {
@@ -160,13 +186,14 @@ func (m *mockNode) AppendParticipationKeys(id account.ParticipationID, keys acco
return m.err
}
-func makeMockNode(ledger v2.LedgerForAPI, genesisID string, nodeError error) *mockNode {
+func makeMockNode(ledger v2.LedgerForAPI, genesisID string, nodeError error, consensusUpgrade bool) *mockNode {
return &mockNode{
- ledger: ledger,
- genesisID: genesisID,
- config: config.GetDefaultLocal(),
- err: nodeError,
- usertxns: map[basics.Address][]node.TxnWithStatus{},
+ ledger: ledger,
+ genesisID: genesisID,
+ config: config.GetDefaultLocal(),
+ err: nodeError,
+ usertxns: map[basics.Address][]node.TxnWithStatus{},
+ consensusUpgrade: consensusUpgrade,
}
}
@@ -174,8 +201,12 @@ func (m *mockNode) LedgerForAPI() v2.LedgerForAPI {
return m.ledger
}
-func (m *mockNode) Status() (s node.StatusReport, err error) {
- s = cannedStatusReportGolden
+func (m mockNode) Status() (s node.StatusReport, err error) {
+ if m.consensusUpgrade {
+ s = cannedStatusReportConsensusUpgradeGolden
+ } else {
+ s = cannedStatusReportGolden
+ }
return
}
func (m *mockNode) GenesisID() string {
@@ -190,6 +221,10 @@ func (m *mockNode) BroadcastSignedTxGroup(txgroup []transactions.SignedTxn) erro
return m.err
}
+func (m *mockNode) Simulate(txgroup []transactions.SignedTxn) (*ledgercore.ValidatedBlock, bool, error) {
+ return nil, false, m.err
+}
+
func (m *mockNode) GetPendingTransaction(txID transactions.Txid) (res node.TxnWithStatus, found bool) {
res = node.TxnWithStatus{}
found = true
@@ -229,10 +264,6 @@ func (m *mockNode) GetTransaction(addr basics.Address, txID transactions.Txid, m
return node.TxnWithStatus{}, false
}
-func (m *mockNode) PoolStats() node.PoolStats {
- return node.PoolStats{}
-}
-
func (m *mockNode) IsArchival() bool {
return false
}
@@ -326,9 +357,9 @@ func testingenv(t testing.TB, numAccounts, numTxs int, offlineAccounts bool) (*d
short := root.Address()
if offlineAccounts && i > P/2 {
- genesis[short] = basics.MakeAccountData(basics.Offline, startamt)
+ genesis[short] = basics_testing.MakeAccountData(basics.Offline, startamt)
} else {
- data := basics.MakeAccountData(basics.Online, startamt)
+ data := basics_testing.MakeAccountData(basics.Online, startamt)
data.SelectionID = parts[i].VRFSecrets().PK
data.VoteID = parts[i].VotingSecrets().OneTimeSignatureVerifier
genesis[short] = data
@@ -336,13 +367,13 @@ func testingenv(t testing.TB, numAccounts, numTxs int, offlineAccounts bool) (*d
part.Close()
}
- genesis[poolAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 100000 * uint64(proto.RewardsRateRefreshInterval)})
+ genesis[poolAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 100000 * uint64(proto.RewardsRateRefreshInterval)})
program := logic.Program(retOneProgram)
lhash := crypto.HashObj(&program)
var addr basics.Address
copy(addr[:], lhash[:])
- ad := basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 100000 * uint64(proto.RewardsRateRefreshInterval)})
+ ad := basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 100000 * uint64(proto.RewardsRateRefreshInterval)})
ad.AppLocalStates = map[basics.AppIndex]basics.AppLocalState{1: {}}
genesis[addr] = ad
diff --git a/daemon/algod/api/server/v2/utils.go b/daemon/algod/api/server/v2/utils.go
index 78db965b5..52690cb8e 100644
--- a/daemon/algod/api/server/v2/utils.go
+++ b/daemon/algod/api/server/v2/utils.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/spec/common/model.go b/daemon/algod/api/spec/common/model.go
index 66741a65b..4b4d4eae7 100644
--- a/daemon/algod/api/spec/common/model.go
+++ b/daemon/algod/api/spec/common/model.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/spec/v2/model.go b/daemon/algod/api/spec/v2/model.go
index 9b997b2b7..845bc7c22 100644
--- a/daemon/algod/api/spec/v2/model.go
+++ b/daemon/algod/api/spec/v2/model.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/api/swagger.go b/daemon/algod/api/swagger.go
index e20506e2c..5402e802e 100644
--- a/daemon/algod/api/swagger.go
+++ b/daemon/algod/api/swagger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/deadlockLogger.go b/daemon/algod/deadlockLogger.go
index 62fc1af4c..923db611f 100644
--- a/daemon/algod/deadlockLogger.go
+++ b/daemon/algod/deadlockLogger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/deadlock_test.go b/daemon/algod/deadlock_test.go
index b3a7b24de..83cc11859 100644
--- a/daemon/algod/deadlock_test.go
+++ b/daemon/algod/deadlock_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/algod/server.go b/daemon/algod/server.go
index 4f1ce3515..44b8a4dcc 100644
--- a/daemon/algod/server.go
+++ b/daemon/algod/server.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -109,18 +109,49 @@ func (s *Server) Initialize(cfg config.Local, phonebookAddresses []string, genes
// Set large enough soft file descriptors limit.
var ot basics.OverflowTracker
- fdRequired := ot.Add(
- cfg.ReservedFDs,
- ot.Add(uint64(cfg.IncomingConnectionsLimit), cfg.RestConnectionsHardLimit))
+ fdRequired := ot.Add(cfg.ReservedFDs, cfg.RestConnectionsHardLimit)
if ot.Overflowed {
return errors.New(
- "Initialize() overflowed when adding up ReservedFDs, IncomingConnectionsLimit " +
- "RestConnectionsHardLimit; decrease them")
+ "Initialize() overflowed when adding up ReservedFDs and RestConnectionsHardLimit; decrease them")
}
err = util.SetFdSoftLimit(fdRequired)
if err != nil {
return fmt.Errorf("Initialize() err: %w", err)
}
+ if cfg.IsGossipServer() {
+ var ot basics.OverflowTracker
+ fdRequired := ot.Add(fdRequired, uint64(cfg.IncomingConnectionsLimit))
+ if ot.Overflowed {
+ return errors.New("Initialize() overflowed when adding up IncomingConnectionsLimit to the existing RLIMIT_NOFILE value; decrease RestConnectionsHardLimit or IncomingConnectionsLimit")
+ }
+ _, hard, err := util.GetFdLimits()
+ if err != nil {
+ s.log.Errorf("Failed to get RLIMIT_NOFILE values: %s", err.Error())
+ } else {
+ maxFDs := fdRequired
+ if fdRequired > hard {
+ // claim as many descriptors are possible
+ maxFDs = hard
+ // but try to keep cfg.ReservedFDs untouched by decreasing other limits
+ if cfg.AdjustConnectionLimits(fdRequired, hard) {
+ s.log.Warnf(
+ "Updated connection limits: RestConnectionsSoftLimit=%d, RestConnectionsHardLimit=%d, IncomingConnectionsLimit=%d",
+ cfg.RestConnectionsSoftLimit,
+ cfg.RestConnectionsHardLimit,
+ cfg.IncomingConnectionsLimit,
+ )
+ if cfg.IncomingConnectionsLimit == 0 {
+ return errors.New("Initialize() failed to adjust connection limits")
+ }
+ }
+ }
+ err = util.SetFdSoftLimit(maxFDs)
+ if err != nil {
+ // do not fail but log the error
+ s.log.Errorf("Failed to set a new RLIMIT_NOFILE value to %d (max %d): %s", fdRequired, hard, err.Error())
+ }
+ }
+ }
// configure the deadlock detector library
switch {
diff --git a/daemon/algod/server_test.go b/daemon/algod/server_test.go
index 145a6e9b6..bb8ec350e 100644
--- a/daemon/algod/server_test.go
+++ b/daemon/algod/server_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/api/api.go b/daemon/kmd/api/api.go
index 182eb19c3..e27c97480 100644
--- a/daemon/kmd/api/api.go
+++ b/daemon/kmd/api/api.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/api/cors.go b/daemon/kmd/api/cors.go
index 89f9bcd89..96c00b7ce 100644
--- a/daemon/kmd/api/cors.go
+++ b/daemon/kmd/api/cors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/api/v1/auth.go b/daemon/kmd/api/v1/auth.go
index 35e73aadb..eb28cb990 100644
--- a/daemon/kmd/api/v1/auth.go
+++ b/daemon/kmd/api/v1/auth.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/api/v1/errors.go b/daemon/kmd/api/v1/errors.go
index 9193ea291..493551f64 100644
--- a/daemon/kmd/api/v1/errors.go
+++ b/daemon/kmd/api/v1/errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/api/v1/handlers.go b/daemon/kmd/api/v1/handlers.go
index 199f1443d..f585cb274 100644
--- a/daemon/kmd/api/v1/handlers.go
+++ b/daemon/kmd/api/v1/handlers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/client/client.go b/daemon/kmd/client/client.go
index 413e3e8d7..339d970f6 100644
--- a/daemon/kmd/client/client.go
+++ b/daemon/kmd/client/client.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/client/requests.go b/daemon/kmd/client/requests.go
index 78da5b4c5..dbd6cf69a 100644
--- a/daemon/kmd/client/requests.go
+++ b/daemon/kmd/client/requests.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/client/wrappers.go b/daemon/kmd/client/wrappers.go
index f3392850c..cb490c791 100644
--- a/daemon/kmd/client/wrappers.go
+++ b/daemon/kmd/client/wrappers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/config/config.go b/daemon/kmd/config/config.go
index 95a03fe0d..3192c7a9a 100644
--- a/daemon/kmd/config/config.go
+++ b/daemon/kmd/config/config.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/config/errors.go b/daemon/kmd/config/errors.go
index 75800791b..510725b09 100644
--- a/daemon/kmd/config/errors.go
+++ b/daemon/kmd/config/errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/kmd.go b/daemon/kmd/kmd.go
index b46fb6beb..e0890c09b 100644
--- a/daemon/kmd/kmd.go
+++ b/daemon/kmd/kmd.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/lib/kmdapi/common.go b/daemon/kmd/lib/kmdapi/common.go
index 58aa755c0..d3b05a115 100644
--- a/daemon/kmd/lib/kmdapi/common.go
+++ b/daemon/kmd/lib/kmdapi/common.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/lib/kmdapi/requests.go b/daemon/kmd/lib/kmdapi/requests.go
index 860d3c4f5..c2d323c78 100644
--- a/daemon/kmd/lib/kmdapi/requests.go
+++ b/daemon/kmd/lib/kmdapi/requests.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/lib/kmdapi/responses.go b/daemon/kmd/lib/kmdapi/responses.go
index f404e843e..4d233e9cb 100644
--- a/daemon/kmd/lib/kmdapi/responses.go
+++ b/daemon/kmd/lib/kmdapi/responses.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/server/errors.go b/daemon/kmd/server/errors.go
index 86396e489..9aac3feeb 100644
--- a/daemon/kmd/server/errors.go
+++ b/daemon/kmd/server/errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/server/server.go b/daemon/kmd/server/server.go
index b36c2859c..021079194 100644
--- a/daemon/kmd/server/server.go
+++ b/daemon/kmd/server/server.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/session/auth.go b/daemon/kmd/session/auth.go
index 3ea9d990f..ecf2db88c 100644
--- a/daemon/kmd/session/auth.go
+++ b/daemon/kmd/session/auth.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/session/session.go b/daemon/kmd/session/session.go
index f1a8b0a52..6aac936d9 100644
--- a/daemon/kmd/session/session.go
+++ b/daemon/kmd/session/session.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/driver/driver.go b/daemon/kmd/wallet/driver/driver.go
index f549d128d..9f1bb0c7f 100644
--- a/daemon/kmd/wallet/driver/driver.go
+++ b/daemon/kmd/wallet/driver/driver.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/driver/ledger.go b/daemon/kmd/wallet/driver/ledger.go
index 1d64a6ab1..e26abe11d 100644
--- a/daemon/kmd/wallet/driver/ledger.go
+++ b/daemon/kmd/wallet/driver/ledger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/driver/ledger_errors.go b/daemon/kmd/wallet/driver/ledger_errors.go
index 420859c83..0fd881108 100644
--- a/daemon/kmd/wallet/driver/ledger_errors.go
+++ b/daemon/kmd/wallet/driver/ledger_errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/driver/ledger_hid.go b/daemon/kmd/wallet/driver/ledger_hid.go
index 28ba4ce5c..36140f918 100644
--- a/daemon/kmd/wallet/driver/ledger_hid.go
+++ b/daemon/kmd/wallet/driver/ledger_hid.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/driver/sqlite.go b/daemon/kmd/wallet/driver/sqlite.go
index 8ad659c28..46992734d 100644
--- a/daemon/kmd/wallet/driver/sqlite.go
+++ b/daemon/kmd/wallet/driver/sqlite.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/driver/sqlite_crypto.go b/daemon/kmd/wallet/driver/sqlite_crypto.go
index 5cca480c9..2749f2683 100644
--- a/daemon/kmd/wallet/driver/sqlite_crypto.go
+++ b/daemon/kmd/wallet/driver/sqlite_crypto.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/driver/sqlite_errors.go b/daemon/kmd/wallet/driver/sqlite_errors.go
index bcfb00fda..39e122a4e 100644
--- a/daemon/kmd/wallet/driver/sqlite_errors.go
+++ b/daemon/kmd/wallet/driver/sqlite_errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/driver/util.go b/daemon/kmd/wallet/driver/util.go
index 2701242e3..7319ae444 100644
--- a/daemon/kmd/wallet/driver/util.go
+++ b/daemon/kmd/wallet/driver/util.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/daemon/kmd/wallet/wallet.go b/daemon/kmd/wallet/wallet.go
index d83770f5d..d4b6b5b1a 100644
--- a/daemon/kmd/wallet/wallet.go
+++ b/daemon/kmd/wallet/wallet.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/account/account.go b/data/account/account.go
index 8e7bb5337..9c0d978c8 100644
--- a/data/account/account.go
+++ b/data/account/account.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/account/partInstall.go b/data/account/partInstall.go
index 2c943935f..0b1087278 100644
--- a/data/account/partInstall.go
+++ b/data/account/partInstall.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/account/participation.go b/data/account/participation.go
index 3209fd7ea..0e1d4564a 100644
--- a/data/account/participation.go
+++ b/data/account/participation.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/account/participationRegistry.go b/data/account/participationRegistry.go
index c8892513e..1381ddbaf 100644
--- a/data/account/participationRegistry.go
+++ b/data/account/participationRegistry.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/account/participationRegistryBench_test.go b/data/account/participationRegistryBench_test.go
index 378950b77..4c6f1098c 100644
--- a/data/account/participationRegistryBench_test.go
+++ b/data/account/participationRegistryBench_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/account/participationRegistry_test.go b/data/account/participationRegistry_test.go
index 286edf117..ef77283ce 100644
--- a/data/account/participationRegistry_test.go
+++ b/data/account/participationRegistry_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -738,7 +738,7 @@ func TestParticipion_Blobs(t *testing.T) {
registry, dbfile := getRegistry(t)
defer registryCloseTest(t, registry, dbfile)
- access, err := db.MakeAccessor("writetest_root", false, true)
+ access, err := db.MakeAccessor(t.Name()+"_writetest_root", false, true)
if err != nil {
panic(err)
}
@@ -746,7 +746,7 @@ func TestParticipion_Blobs(t *testing.T) {
access.Close()
a.NoError(err)
- access, err = db.MakeAccessor("writetest", false, true)
+ access, err = db.MakeAccessor(t.Name()+"_writetest", false, true)
if err != nil {
panic(err)
}
@@ -781,7 +781,7 @@ func TestParticipion_EmptyBlobs(t *testing.T) {
registry, dbfile := getRegistry(t)
defer registryCloseTest(t, registry, dbfile)
- access, err := db.MakeAccessor("writetest_root", false, true)
+ access, err := db.MakeAccessor(t.Name()+"_writetest_root", false, true)
if err != nil {
panic(err)
}
@@ -789,7 +789,7 @@ func TestParticipion_EmptyBlobs(t *testing.T) {
access.Close()
a.NoError(err)
- access, err = db.MakeAccessor("writetest", false, true)
+ access, err = db.MakeAccessor(t.Name()+"_writetest", false, true)
if err != nil {
panic(err)
}
@@ -963,7 +963,7 @@ func TestGetRoundSecretsWithNilStateProofVerifier(t *testing.T) {
registry, dbfile := getRegistry(t)
defer registryCloseTest(t, registry, dbfile)
- access, err := db.MakeAccessor("stateprooftest", false, true)
+ access, err := db.MakeAccessor(t.Name()+"_stateprooftest", false, true)
if err != nil {
panic(err)
}
@@ -1012,7 +1012,7 @@ func TestAddingSecretTwice(t *testing.T) {
registry, dbfile := getRegistry(t)
defer registryCloseTest(t, registry, dbfile)
- access, err := db.MakeAccessor("stateprooftest", false, true)
+ access, err := db.MakeAccessor(t.Name()+"_stateprooftest", false, true)
if err != nil {
panic(err)
}
@@ -1050,7 +1050,7 @@ func TestGetRoundSecretsWithoutStateProof(t *testing.T) {
registry, dbfile := getRegistry(t)
defer registryCloseTest(t, registry, dbfile)
- access, err := db.MakeAccessor("stateprooftest", false, true)
+ access, err := db.MakeAccessor(t.Name()+"_stateprooftest", false, true)
if err != nil {
panic(err)
}
@@ -1177,7 +1177,7 @@ func TestFlushResetsLastError(t *testing.T) {
registry, dbfile := getRegistry(t)
defer registryCloseTest(t, registry, dbfile)
- access, err := db.MakeAccessor("stateprooftest", false, true)
+ access, err := db.MakeAccessor(t.Name()+"_stateprooftest", false, true)
a.NoError(err)
root, err := GenerateRoot(access)
diff --git a/data/account/participation_test.go b/data/account/participation_test.go
index 9a48bb7a0..b9a417a8f 100644
--- a/data/account/participation_test.go
+++ b/data/account/participation_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -531,7 +531,7 @@ func TestFillDBWithParticipationKeys(t *testing.T) {
a.NoError(err)
}
-func TestKeyregValidityPeriod(t *testing.T) {
+func TestKeyregValidityPeriod(t *testing.T) { //nolint:paralleltest // Not parallel because it modifies config.Consensus
partitiontest.PartitionTest(t)
a := require.New(t)
diff --git a/data/account/registeryDbOps.go b/data/account/registeryDbOps.go
index f83055d3d..e6722e5eb 100644
--- a/data/account/registeryDbOps.go
+++ b/data/account/registeryDbOps.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/account/rootInstall.go b/data/account/rootInstall.go
index 4bf9a73e2..50f1c6e97 100644
--- a/data/account/rootInstall.go
+++ b/data/account/rootInstall.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/accountManager.go b/data/accountManager.go
index 39998a09d..222a45944 100644
--- a/data/accountManager.go
+++ b/data/accountManager.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/accountManager_test.go b/data/accountManager_test.go
index 1fcfe56bf..0be2ec139 100644
--- a/data/accountManager_test.go
+++ b/data/accountManager_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -43,6 +43,11 @@ import (
func TestAccountManagerKeys(t *testing.T) {
partitiontest.PartitionTest(t)
+ if testing.Short() {
+ t.Log("this is a long test and skipping for -short")
+ return
+ }
+
registry := &mocks.MockParticipationRegistry{}
testAccountManagerKeys(t, registry, false)
}
@@ -85,6 +90,11 @@ func registryCloseTest(t testing.TB, registry account.ParticipationRegistry, dbf
func TestAccountManagerKeysRegistry(t *testing.T) {
partitiontest.PartitionTest(t)
+ if testing.Short() {
+ t.Log("this is a long test and skipping for -short")
+ return
+ }
+
registry, dbName := getRegistryImpl(t, false, true)
defer registryCloseTest(t, registry, dbName)
testAccountManagerKeys(t, registry, true)
@@ -123,6 +133,7 @@ func testAccountManagerKeys(t *testing.T, registry account.ParticipationRegistry
accessor, err := db.MakeErasableAccessor(partFilename)
require.NoError(t, err)
+ defer accessor.Close()
accessor.SetLogger(log)
part, err := account.FillDBWithParticipationKeys(accessor, root.Address(), 0, 100, 10000)
diff --git a/data/basics/address.go b/data/basics/address.go
index 412b7bf75..6e2249f4f 100644
--- a/data/basics/address.go
+++ b/data/basics/address.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/address_test.go b/data/basics/address_test.go
index eac7f09e5..264b90a56 100644
--- a/data/basics/address_test.go
+++ b/data/basics/address_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/fields_test.go b/data/basics/fields_test.go
index c3cf25284..4baacdb16 100644
--- a/data/basics/fields_test.go
+++ b/data/basics/fields_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/overflow.go b/data/basics/overflow.go
index 5296fd884..c52f99572 100644
--- a/data/basics/overflow.go
+++ b/data/basics/overflow.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/sort.go b/data/basics/sort.go
index 6841a6788..0d3c75d9d 100644
--- a/data/basics/sort.go
+++ b/data/basics/sort.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/stateProofParticipant.go b/data/basics/stateProofParticipant.go
index 5dcc3ad85..a2c4fc490 100644
--- a/data/basics/stateProofParticipant.go
+++ b/data/basics/stateProofParticipant.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/teal.go b/data/basics/teal.go
index 1aaac047f..9f247f2f3 100644
--- a/data/basics/teal.go
+++ b/data/basics/teal.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/teal_test.go b/data/basics/teal_test.go
index 9090379d6..6703faeec 100644
--- a/data/basics/teal_test.go
+++ b/data/basics/teal_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/telemetryspec/operation.go b/data/basics/testing/userBalance.go
index 037e8fca3..13efdf5d5 100644
--- a/logging/telemetryspec/operation.go
+++ b/data/basics/testing/userBalance.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -14,10 +14,18 @@
// 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 telemetryspec
+package testing
-// Telemetry operations
+import (
+ "github.com/algorand/go-algorand/data/basics"
+)
-// Operation is the type used to identify strings used for telemetry operation identifiers.
-// We want these to be stable and easy to find / document so we can create queries against them.
-type Operation string
+// MakeAccountData returns a AccountData with non-empty voting fields for online accounts
+func MakeAccountData(status basics.Status, algos basics.MicroAlgos) basics.AccountData {
+ ad := basics.AccountData{Status: status, MicroAlgos: algos}
+ if status == basics.Online {
+ ad.VoteFirstValid = 1
+ ad.VoteLastValid = 100_000
+ }
+ return ad
+}
diff --git a/data/basics/units.go b/data/basics/units.go
index c96b08233..de23f533a 100644
--- a/data/basics/units.go
+++ b/data/basics/units.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/units_test.go b/data/basics/units_test.go
index 5e97f4ca5..b5c698e46 100644
--- a/data/basics/units_test.go
+++ b/data/basics/units_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go
index 06d7c4b6b..f10c32635 100644
--- a/data/basics/userBalance.go
+++ b/data/basics/userBalance.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -414,11 +414,6 @@ func (app AppIndex) Address() Address {
return Address(crypto.HashObj(app))
}
-// MakeAccountData returns a UserToken
-func MakeAccountData(status Status, algos MicroAlgos) AccountData {
- return AccountData{Status: status, MicroAlgos: algos}
-}
-
// Money returns the amount of MicroAlgos associated with the user's account
func (u AccountData) Money(proto config.ConsensusParams, rewardsLevel uint64) (money MicroAlgos, rewards MicroAlgos) {
e := u.WithUpdatedRewards(proto, rewardsLevel)
diff --git a/data/basics/userBalance_test.go b/data/basics/userBalance_test.go
index 347dadfe6..912c75aed 100644
--- a/data/basics/userBalance_test.go
+++ b/data/basics/userBalance_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go
index 702344235..344148afa 100644
--- a/data/bookkeeping/block.go
+++ b/data/bookkeeping/block.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go
index 5330c85cd..2b4fa8c81 100644
--- a/data/bookkeeping/block_test.go
+++ b/data/bookkeeping/block_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -206,7 +206,7 @@ func TestMakeBlockUpgrades(t *testing.T) {
require.Equal(t, bd2.NextProtocolSwitchOn-bd2.NextProtocolVoteBefore, basics.Round(5))
}
-func TestBlockUnsupported(t *testing.T) {
+func TestBlockUnsupported(t *testing.T) { //nolint:paralleltest // Not parallel because it modifies config.Consensus
partitiontest.PartitionTest(t)
var b Block
diff --git a/data/bookkeeping/encoding_test.go b/data/bookkeeping/encoding_test.go
index e892bb502..95ccfbbca 100644
--- a/data/bookkeeping/encoding_test.go
+++ b/data/bookkeeping/encoding_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/genesis.go b/data/bookkeeping/genesis.go
index 7f01519e8..a81103f28 100644
--- a/data/bookkeeping/genesis.go
+++ b/data/bookkeeping/genesis.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/genesis_test.go b/data/bookkeeping/genesis_test.go
index 9ca60bd5e..5810e079d 100644
--- a/data/bookkeeping/genesis_test.go
+++ b/data/bookkeeping/genesis_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/lightBlockHeader.go b/data/bookkeeping/lightBlockHeader.go
index e9530faa1..ea283e039 100644
--- a/data/bookkeeping/lightBlockHeader.go
+++ b/data/bookkeeping/lightBlockHeader.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/lightBlockHeader_test.go b/data/bookkeeping/lightBlockHeader_test.go
index c9d39c008..50d561c47 100644
--- a/data/bookkeeping/lightBlockHeader_test.go
+++ b/data/bookkeeping/lightBlockHeader_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/prettyprinting.go b/data/bookkeeping/prettyprinting.go
index 7c8e28a0a..96c259a81 100644
--- a/data/bookkeeping/prettyprinting.go
+++ b/data/bookkeeping/prettyprinting.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/txn_merkle.go b/data/bookkeeping/txn_merkle.go
index cfaf95ad8..7ab84d029 100644
--- a/data/bookkeeping/txn_merkle.go
+++ b/data/bookkeeping/txn_merkle.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/bookkeeping/txn_merkle_test.go b/data/bookkeeping/txn_merkle_test.go
index 4ead543da..22df9025a 100644
--- a/data/bookkeeping/txn_merkle_test.go
+++ b/data/bookkeeping/txn_merkle_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/committee/committee.go b/data/committee/committee.go
index d70eca646..263aac999 100644
--- a/data/committee/committee.go
+++ b/data/committee/committee.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/committee/common_test.go b/data/committee/common_test.go
index b0b77efd1..89e72fd79 100644
--- a/data/committee/common_test.go
+++ b/data/committee/common_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/committee/credential.go b/data/committee/credential.go
index c725cc9a8..8debf212a 100644
--- a/data/committee/credential.go
+++ b/data/committee/credential.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/committee/credential_test.go b/data/committee/credential_test.go
index fe13be8c1..0e3c3ea40 100644
--- a/data/committee/credential_test.go
+++ b/data/committee/credential_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/committee/encoding_test.go b/data/committee/encoding_test.go
index ae34120de..be1a5ad9e 100644
--- a/data/committee/encoding_test.go
+++ b/data/committee/encoding_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/committee/sortition/sortition.go b/data/committee/sortition/sortition.go
index 5b8300add..05f3476f7 100644
--- a/data/committee/sortition/sortition.go
+++ b/data/committee/sortition/sortition.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/committee/sortition/sortition_test.go b/data/committee/sortition/sortition_test.go
index 81bf89398..6d4685762 100644
--- a/data/committee/sortition/sortition_test.go
+++ b/data/committee/sortition/sortition_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/common_test.go b/data/common_test.go
index ab1004fc1..0ba58ad8d 100644
--- a/data/common_test.go
+++ b/data/common_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -26,6 +26,7 @@ import (
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/account"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/logging"
@@ -103,16 +104,16 @@ func testingenv(t testing.TB, numAccounts, numTxs int, offlineAccounts bool) (*L
short := root.Address()
if offlineAccounts && i > P/2 {
- genesis[short] = basics.MakeAccountData(basics.Offline, startamt)
+ genesis[short] = basics_testing.MakeAccountData(basics.Offline, startamt)
} else {
- data := basics.MakeAccountData(basics.Online, startamt)
+ data := basics_testing.MakeAccountData(basics.Online, startamt)
data.SelectionID = parts[i].VRFSecrets().PK
data.VoteID = parts[i].VotingSecrets().OneTimeSignatureVerifier
genesis[short] = data
}
}
- genesis[poolAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 100000 * uint64(proto.RewardsRateRefreshInterval)})
+ genesis[poolAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 100000 * uint64(proto.RewardsRateRefreshInterval)})
bootstrap := bookkeeping.MakeGenesisBalances(genesis, poolAddr, sinkAddr)
diff --git a/data/datatest/fabricateLedger.go b/data/datatest/fabricateLedger.go
index 9c859bbee..8bd1c3f25 100644
--- a/data/datatest/fabricateLedger.go
+++ b/data/datatest/fabricateLedger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/datatest/impls.go b/data/datatest/impls.go
index 7a8f804ea..960b12e37 100644
--- a/data/datatest/impls.go
+++ b/data/datatest/impls.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/hashable/message.go b/data/hashable/message.go
index ae0ae7806..bfa032633 100644
--- a/data/hashable/message.go
+++ b/data/hashable/message.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/ledger.go b/data/ledger.go
index 101da721a..04e532061 100644
--- a/data/ledger.go
+++ b/data/ledger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -80,7 +80,7 @@ type roundSeed struct {
func LoadLedger(
log logging.Logger, dbFilenamePrefix string, memory bool,
genesisProto protocol.ConsensusVersion, genesisBal bookkeeping.GenesisBalances, genesisID string, genesisHash crypto.Digest,
- blockListeners []ledger.BlockListener, cfg config.Local,
+ blockListeners []ledgercore.BlockListener, cfg config.Local,
) (*Ledger, error) {
if genesisBal.Balances == nil {
genesisBal.Balances = make(map[basics.Address]basics.AccountData)
diff --git a/data/ledger_test.go b/data/ledger_test.go
index 9b74ddc85..540dfea1d 100644
--- a/data/ledger_test.go
+++ b/data/ledger_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -28,6 +28,7 @@ import (
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/ledger"
@@ -78,12 +79,12 @@ func testGenerateInitState(tb testing.TB, proto protocol.ConsensusVersion) (gene
if i%2 == 0 {
accountStatus = basics.NotParticipating
}
- initAccounts[genaddrs[i]] = basics.MakeAccountData(accountStatus, basics.MicroAlgos{Raw: uint64((i + 100) * 100000)})
+ initAccounts[genaddrs[i]] = basics_testing.MakeAccountData(accountStatus, basics.MicroAlgos{Raw: uint64((i + 100) * 100000)})
}
initKeys[poolAddr] = poolSecret
- initAccounts[poolAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 1234567})
+ initAccounts[poolAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 1234567})
initKeys[sinkAddr] = sinkSecret
- initAccounts[sinkAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 7654321})
+ initAccounts[sinkAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 7654321})
incentivePoolBalanceAtGenesis := initAccounts[poolAddr].MicroAlgos
initialRewardsPerRound := incentivePoolBalanceAtGenesis.Raw / uint64(params.RewardsRateRefreshInterval)
@@ -326,6 +327,10 @@ func TestLedgerSeed(t *testing.T) {
func TestConsensusVersion(t *testing.T) {
partitiontest.PartitionTest(t)
+ if testing.Short() {
+ t.Log("this is a long test and skipping for -short")
+ return
+ }
// find a consensus protocol that leads to ConsensusCurrentVersion
var previousProtocol protocol.ConsensusVersion
@@ -491,8 +496,8 @@ func TestLedgerErrorValidate(t *testing.T) {
blk.BlockHeader.GenesisHash = crypto.Hash([]byte(t.Name()))
accts := make(map[basics.Address]basics.AccountData)
- accts[testPoolAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 0})
- accts[testSinkAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 0})
+ accts[testPoolAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 0})
+ accts[testSinkAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 0})
genesisInitState := ledgercore.InitState{
Accounts: accts,
@@ -513,7 +518,7 @@ func TestLedgerErrorValidate(t *testing.T) {
defer realLedger.Close()
l := Ledger{Ledger: realLedger, log: log}
- l.log.SetLevel(logging.Debug)
+ l.log.SetLevel(logging.Warn)
require.NotNil(t, &l)
totalsRound, _, err := realLedger.LatestTotals()
diff --git a/data/pools/errors.go b/data/pools/errors.go
index e465983fa..819678b2c 100644
--- a/data/pools/errors.go
+++ b/data/pools/errors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/pools/statusCache.go b/data/pools/statusCache.go
index 3b25f7f67..0b8d96741 100644
--- a/data/pools/statusCache.go
+++ b/data/pools/statusCache.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/pools/transactionPool.go b/data/pools/transactionPool.go
index 44852c345..bed4e17e6 100644
--- a/data/pools/transactionPool.go
+++ b/data/pools/transactionPool.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/pools/transactionPool_test.go b/data/pools/transactionPool_test.go
index be0546c3b..02239d50f 100644
--- a/data/pools/transactionPool_test.go
+++ b/data/pools/transactionPool_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/stateproofmsg/message.go b/data/stateproofmsg/message.go
index aea195475..5f1c2e343 100644
--- a/data/stateproofmsg/message.go
+++ b/data/stateproofmsg/message.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/application.go b/data/transactions/application.go
index 70fd774df..6ae70ed65 100644
--- a/data/transactions/application.go
+++ b/data/transactions/application.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/application_test.go b/data/transactions/application_test.go
index 64777185c..24bff87a0 100644
--- a/data/transactions/application_test.go
+++ b/data/transactions/application_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/asset.go b/data/transactions/asset.go
index 9daafd5bc..c15ec3db9 100644
--- a/data/transactions/asset.go
+++ b/data/transactions/asset.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/common_test.go b/data/transactions/common_test.go
index b600ce6cf..0509241e6 100644
--- a/data/transactions/common_test.go
+++ b/data/transactions/common_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/error.go b/data/transactions/error.go
index 86ce01102..4ce693cf9 100644
--- a/data/transactions/error.go
+++ b/data/transactions/error.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/json_test.go b/data/transactions/json_test.go
index 547224fb6..1e5474e54 100644
--- a/data/transactions/json_test.go
+++ b/data/transactions/json_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -30,6 +30,7 @@ import (
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/txntest"
"github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/test/partitiontest"
"github.com/stretchr/testify/require"
)
@@ -45,6 +46,8 @@ func compact(data []byte) string {
// TestJsonMarshal ensures that BoxRef names are b64 encoded, since they may not be characters.
func TestJsonMarshal(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
marshal := protocol.EncodeJSON(transactions.BoxRef{Index: 4, Name: []byte("joe")})
require.Equal(t, `{"i":4,"n":"am9l"}`, compact(marshal))
@@ -60,6 +63,7 @@ func TestJsonMarshal(t *testing.T) {
// TestJsonUnmarshal ensures that BoxRef unmarshaling expects b64 names
func TestJsonUnmarshal(t *testing.T) {
+ partitiontest.PartitionTest(t)
var br transactions.BoxRef
decode(t, `{"i":4,"n":"am9l"}`, &br)
@@ -82,6 +86,8 @@ func TestJsonUnmarshal(t *testing.T) {
// encoded. These things could change without breaking the protocol, should stay
// the same for the sake of REST API compatibility.
func TestTxnJson(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
txn := txntest.Txn{
Sender: basics.Address{0x01, 0x02, 0x03},
}
diff --git a/data/transactions/keyreg.go b/data/transactions/keyreg.go
index 401617ef7..6e917ca11 100644
--- a/data/transactions/keyreg.go
+++ b/data/transactions/keyreg.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/TEAL_opcodes.md b/data/transactions/logic/TEAL_opcodes.md
index cd2bd5842..54a80db8f 100644
--- a/data/transactions/logic/TEAL_opcodes.md
+++ b/data/transactions/logic/TEAL_opcodes.md
@@ -280,7 +280,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u
## bytecblock bytes ...
-- Opcode: 0x26 {varuint count} [({varuint value length} bytes), ...]
+- Opcode: 0x26 {varuint count} [({varuint length} bytes), ...]
- Stack: ... &rarr; ...
- prepare block of byte-array constants for use by bytec
@@ -318,7 +318,7 @@ The notation J,K indicates that two uint64 values J and K are interpreted as a u
## arg n
-- Opcode: 0x2c {uint8 arg index N}
+- Opcode: 0x2c {uint8 arg index}
- Stack: ... &rarr; ..., []byte
- Nth LogicSig argument
- Mode: Signature
@@ -811,7 +811,7 @@ When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on
## json_ref r
-- Opcode: 0x5f {uint8 return type}
+- Opcode: 0x5f {uint8 return type index}
- Stack: ..., A: []byte, B: []byte &rarr; ..., any
- key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A
- **Cost**: 25 + 2 per 7 bytes of A
@@ -1059,7 +1059,7 @@ pushint args are not added to the intcblock during assembly processes
## pushbytess bytes ...
-- Opcode: 0x82 {varuint count} [({varuint value length} bytes), ...]
+- Opcode: 0x82 {varuint count} [({varuint length} bytes), ...]
- Stack: ... &rarr; ..., [N items]
- push sequences of immediate byte arrays to stack (first byte array being deepest)
- Availability: v8
@@ -1548,7 +1548,7 @@ For boxes that exceed 4,096 bytes, consider `box_create`, `box_extract`, and `bo
## block f
-- Opcode: 0xd1 {uint8 block field}
+- Opcode: 0xd1 {uint8 block field index}
- Stack: ..., A: uint64 &rarr; ..., any
- field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)
- Availability: v7
diff --git a/data/transactions/logic/assembler.go b/data/transactions/logic/assembler.go
index e25e806be..45489918a 100644
--- a/data/transactions/logic/assembler.go
+++ b/data/transactions/logic/assembler.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -1622,7 +1622,7 @@ func isFullSpec(spec OpSpec) bool {
}
// mergeProtos allows us to support typetracking of pseudo-ops which are given an improper number of immediates
-//by creating a new proto that is a combination of all the pseudo-op's possibilities
+// by creating a new proto that is a combination of all the pseudo-op's possibilities
func mergeProtos(specs map[int]OpSpec) (Proto, uint64, bool) {
var args StackTypes
var returns StackTypes
@@ -1857,10 +1857,13 @@ func splitTokens(tokens []string) (current, rest []string) {
// assemble reads text from an input and accumulates the program
func (ops *OpStream) assemble(text string) error {
- fin := strings.NewReader(text)
if ops.Version > LogicVersion && ops.Version != assemblerNoVersion {
return ops.errorf("Can not assemble version %d", ops.Version)
}
+ if strings.TrimSpace(text) == "" {
+ return ops.errorf("Cannot assemble empty program text")
+ }
+ fin := strings.NewReader(text)
scanner := bufio.NewScanner(fin)
for scanner.Scan() {
ops.sourceLine++
@@ -1904,7 +1907,7 @@ func (ops *OpStream) assemble(text string) error {
if ok {
ops.trace("%3d: %s\t", ops.sourceLine, opstring)
ops.recordSourceLine()
- if spec.Modes == modeApp {
+ if spec.Modes == ModeApp {
ops.HasStatefulOps = true
}
args, returns := spec.Arg.Types, spec.Return.Types
@@ -2411,8 +2414,19 @@ func (ops *OpStream) warnf(format string, a ...interface{}) error {
return ops.warn(fmt.Errorf(format, a...))
}
-// ReportProblems issues accumulated warnings and outputs errors to an io.Writer.
-func (ops *OpStream) ReportProblems(fname string, writer io.Writer) {
+// ReportMultipleErrors issues accumulated warnings and outputs errors to an io.Writer.
+// In the case of exactly 1 error and no warnings, a slightly different format is provided
+// to handle the cases when the original error is or isn't reported elsewhere.
+// In the case of > 10 errors, only the first 10 errors will be reported.
+func (ops *OpStream) ReportMultipleErrors(fname string, writer io.Writer) {
+ if len(ops.Errors) == 1 && len(ops.Warnings) == 0 {
+ prefix := ""
+ if fname != "" {
+ prefix = fmt.Sprintf("%s: ", fname)
+ }
+ fmt.Fprintf(writer, "%s1 error: %s\n", prefix, ops.Errors[0])
+ return
+ }
for i, e := range ops.Errors {
if i > 9 {
break
@@ -2789,7 +2803,7 @@ func disassembleInstrumented(program []byte, labels map[int]string) (text string
return
}
op := opsByOpcode[version][program[dis.pc]]
- if op.Modes == modeApp {
+ if op.Modes == ModeApp {
ds.hasStatefulOps = true
}
if op.Name == "" {
diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go
index d53b6fc16..cc5cd97a4 100644
--- a/data/transactions/logic/assembler_test.go
+++ b/data/transactions/logic/assembler_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -19,6 +19,7 @@ package logic
import (
"bytes"
"encoding/hex"
+ "errors"
"fmt"
"regexp"
"strings"
@@ -1457,7 +1458,7 @@ done:`
require.Equal(t, expectedProgBytes, ops.Program)
}
-func TestMultipleErrors(t *testing.T) {
+func TestSeveralErrors(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
@@ -1623,10 +1624,12 @@ func TestAssembleDisassembleCycle(t *testing.T) {
// catch any suprises.
require.LessOrEqual(t, LogicVersion, len(nonsense)) // Allow nonsense for future versions
for v, source := range nonsense {
+ v, source := v, source
if v > LogicVersion {
continue // We allow them to be set, but can't test assembly beyond LogicVersion
}
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
+ t.Parallel()
ops := testProg(t, source, v)
t2, err := Disassemble(ops.Program)
require.NoError(t, err)
@@ -3051,3 +3054,108 @@ func TestAssemblePushConsts(t *testing.T) {
source = `pushbytess "x" "y"; +`
testProg(t, source, AssemblerMaxVersion, Expect{1, "+ arg 1 wanted type uint64 got []byte"})
}
+
+func TestAssembleEmpty(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ emptyExpect := Expect{0, "Cannot assemble empty program text"}
+ emptyPrograms := []string{
+ "",
+ " ",
+ " \n\t\t\t\n\n ",
+ " \n \t \t \t \n \n \n\n",
+ }
+
+ nonEmpty := " \n \t \t \t int 1 \n \n \t \t \n\n"
+
+ for version := uint64(1); version <= AssemblerMaxVersion; version++ {
+ for _, prog := range emptyPrograms {
+ testProg(t, prog, version, emptyExpect)
+ }
+ testProg(t, nonEmpty, version)
+ }
+}
+
+func TestReportMultipleErrors(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ assertWithMsg := func(t *testing.T, expectedOutput string, b bytes.Buffer) {
+ if b.String() != expectedOutput {
+ t.Errorf("Unexpected output: got %q, want %q", b.String(), expectedOutput)
+ }
+ }
+
+ ops := &OpStream{
+ Errors: []lineError{
+ {Line: 1, Err: errors.New("error 1")},
+ {Err: errors.New("error 2")},
+ {Line: 3, Err: errors.New("error 3")},
+ },
+ Warnings: []error{
+ errors.New("warning 1"),
+ errors.New("warning 2"),
+ },
+ }
+
+ // Test the case where fname is not empty
+ var b bytes.Buffer
+ ops.ReportMultipleErrors("test.txt", &b)
+ expected := `test.txt: 1: error 1
+test.txt: 0: error 2
+test.txt: 3: error 3
+test.txt: warning 1
+test.txt: warning 2
+`
+ assertWithMsg(t, expected, b)
+
+ // Test the case where fname is empty
+ b.Reset()
+ ops.ReportMultipleErrors("", &b)
+ expected = `1: error 1
+0: error 2
+3: error 3
+warning 1
+warning 2
+`
+ assertWithMsg(t, expected, b)
+
+ // no errors or warnings at all
+ ops = &OpStream{}
+ b.Reset()
+ ops.ReportMultipleErrors("blah blah", &b)
+ expected = ""
+ assertWithMsg(t, expected, b)
+
+ // more than 10 errors:
+ file := "great-file.go"
+ les := []lineError{}
+ expectedStrs := []string{}
+ for i := 1; i <= 11; i++ {
+ errS := fmt.Errorf("error %d", i)
+ les = append(les, lineError{i, errS})
+ if i <= 10 {
+ expectedStrs = append(expectedStrs, fmt.Sprintf("%s: %d: %s", file, i, errS))
+ }
+ }
+ expected = strings.Join(expectedStrs, "\n") + "\n"
+ ops = &OpStream{Errors: les}
+ b.Reset()
+ ops.ReportMultipleErrors(file, &b)
+ assertWithMsg(t, expected, b)
+
+ // exactly 1 error + filename
+ ops = &OpStream{Errors: []lineError{{42, errors.New("super annoying error")}}}
+ b.Reset()
+ ops.ReportMultipleErrors("galaxy.py", &b)
+ expected = "galaxy.py: 1 error: 42: super annoying error\n"
+ assertWithMsg(t, expected, b)
+
+ // exactly 1 error w/o filename
+ ops = &OpStream{Errors: []lineError{{42, errors.New("super annoying error")}}}
+ b.Reset()
+ ops.ReportMultipleErrors("", &b)
+ expected = "1 error: 42: super annoying error\n"
+ assertWithMsg(t, expected, b)
+}
diff --git a/data/transactions/logic/backwardCompat_test.go b/data/transactions/logic/backwardCompat_test.go
index 04fff1828..df6bd6821 100644
--- a/data/transactions/logic/backwardCompat_test.go
+++ b/data/transactions/logic/backwardCompat_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -467,19 +467,24 @@ func TestBackwardCompatAssemble(t *testing.T) {
source := "int 1; int 1; bnz done; done:"
t.Run("v=default", func(t *testing.T) {
+ t.Parallel()
testProg(t, source, assemblerNoVersion, Expect{1, "label \"done\" is too far away"})
})
t.Run("v=default", func(t *testing.T) {
+ t.Parallel()
testProg(t, source, 0, Expect{1, "label \"done\" is too far away"})
})
t.Run("v=default", func(t *testing.T) {
+ t.Parallel()
testProg(t, source, 1, Expect{1, "label \"done\" is too far away"})
})
for v := uint64(2); v <= AssemblerMaxVersion; v++ {
+ v := v
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
+ t.Parallel()
testLogic(t, source, v, defaultEvalParams())
})
}
@@ -487,6 +492,7 @@ func TestBackwardCompatAssemble(t *testing.T) {
func TestExplicitConstants(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
require.Equal(t, 4096, maxStringSize, "constant changed, make it version dependent")
require.Equal(t, 64, maxByteMathSize, "constant changed, move it version dependent")
diff --git a/data/transactions/logic/blackbox_test.go b/data/transactions/logic/blackbox_test.go
index e26f84a8f..79029f2c3 100644
--- a/data/transactions/logic/blackbox_test.go
+++ b/data/transactions/logic/blackbox_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -34,6 +34,7 @@ import (
func TestNewAppEvalParams(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
params := []config.ConsensusParams{
{Application: true, MaxAppProgramCost: 700},
@@ -78,8 +79,11 @@ func TestNewAppEvalParams(t *testing.T) {
}
for i, param := range params {
+ param := param
for j, testCase := range cases {
+ i, j, param, testCase := i, j, param, testCase
t.Run(fmt.Sprintf("i=%d,j=%d", i, j), func(t *testing.T) {
+ t.Parallel()
ep := logic.NewEvalParams(testCase.group, &param, nil)
require.NotNil(t, ep)
require.Equal(t, ep.TxnGroup, testCase.group)
diff --git a/data/transactions/logic/box.go b/data/transactions/logic/box.go
index 6f2e9ccd9..ebc1c25f2 100644
--- a/data/transactions/logic/box.go
+++ b/data/transactions/logic/box.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -17,7 +17,6 @@
package logic
import (
- "encoding/binary"
"fmt"
"github.com/algorand/go-algorand/data/basics"
@@ -283,36 +282,3 @@ func opBoxPut(cx *EvalContext) error {
appAddr := cx.getApplicationAddress(cx.appID)
return cx.Ledger.NewBox(cx.appID, name, value, appAddr)
}
-
-const boxPrefix = "bx:"
-const boxPrefixLength = len(boxPrefix)
-const boxNameIndex = boxPrefixLength + 8 // len("bx:") + 8 (appIdx, big-endian)
-
-// MakeBoxKey creates the key that a box named `name` under app `appIdx` should use.
-func MakeBoxKey(appIdx basics.AppIndex, name string) string {
- /* This format is chosen so that a simple indexing scheme on the key would
- allow for quick lookups of all the boxes of a certain app, or even all
- the boxes of a certain app with a certain prefix.
-
- The "bx:" prefix is so that the kvstore might be usable for things
- besides boxes.
- */
- key := make([]byte, boxNameIndex+len(name))
- copy(key, boxPrefix)
- binary.BigEndian.PutUint64(key[boxPrefixLength:], uint64(appIdx))
- copy(key[boxNameIndex:], name)
- return string(key)
-}
-
-// SplitBoxKey extracts an appid and box name from a string that was created by MakeBoxKey()
-func SplitBoxKey(key string) (basics.AppIndex, string, error) {
- if len(key) < boxNameIndex {
- return 0, "", fmt.Errorf("SplitBoxKey() cannot extract AppIndex as key (%s) too short (length=%d)", key, len(key))
- }
- if key[:boxPrefixLength] != boxPrefix {
- return 0, "", fmt.Errorf("SplitBoxKey() illegal app box prefix in key (%s). Expected prefix '%s'", key, boxPrefix)
- }
- keyBytes := []byte(key)
- app := basics.AppIndex(binary.BigEndian.Uint64(keyBytes[boxPrefixLength:boxNameIndex]))
- return app, key[boxNameIndex:], nil
-}
diff --git a/data/transactions/logic/box_test.go b/data/transactions/logic/box_test.go
index 515f0ad69..77c3adf1b 100644
--- a/data/transactions/logic/box_test.go
+++ b/data/transactions/logic/box_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -27,17 +27,19 @@ import (
"github.com/algorand/go-algorand/data/txntest"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/test/partitiontest"
- "github.com/stretchr/testify/require"
)
func TestBoxNewDel(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- ep, txn, ledger := logic.MakeSampleEnv()
-
for _, size := range []int{24, 0} {
+ size := size
t.Run(fmt.Sprintf("box size=%d", size), func(t *testing.T) {
+ t.Parallel()
+
+ ep, txn, ledger := logic.MakeSampleEnv()
+
createSelf := fmt.Sprintf(`byte "self"; int %d; box_create;`, size)
createOther := fmt.Sprintf(`byte "other"; int %d; box_create;`, size)
@@ -218,7 +220,9 @@ func TestBoxUnavailableWithClearState(t *testing.T) {
}
for name, program := range tests {
+ name, program := name, program
t.Run(name, func(t *testing.T) {
+ t.Parallel()
ep, _, l := logic.MakeSampleEnv()
l.NewApp(basics.Address{}, 888, basics.AppParams{})
ep.TxnGroup[0].Txn.OnCompletion = transactions.ClearStateOC
@@ -521,18 +525,23 @@ func TestEarlyPanics(t *testing.T) {
"box_replace": `byte "%s"; int 0; byte "new"; box_replace`,
}
- ep, _, l := logic.MakeSampleEnv()
- l.NewApp(basics.Address{}, 888, basics.AppParams{})
-
for name, program := range tests {
+ name, program := name, program
t.Run(name+"/zero", func(t *testing.T) {
+ t.Parallel()
+ ep, _, l := logic.MakeSampleEnv()
+ l.NewApp(basics.Address{}, 888, basics.AppParams{})
logic.TestApp(t, fmt.Sprintf(program, ""), ep, "zero length")
})
}
big := strings.Repeat("x", 65)
for name, program := range tests {
+ name, program := name, program
t.Run(name+"/long", func(t *testing.T) {
+ t.Parallel()
+ ep, _, l := logic.MakeSampleEnv()
+ l.NewApp(basics.Address{}, 888, basics.AppParams{})
logic.TestApp(t, fmt.Sprintf(program, big), ep, "name too long")
})
}
@@ -558,45 +567,3 @@ func TestBoxTotals(t *testing.T) {
logic.TestApp(t, `int 888; app_params_get AppAddress; assert;
acct_params_get AcctTotalBoxBytes; pop; int 35; ==`, ep)
}
-
-func TestMakeBoxKey(t *testing.T) {
- partitiontest.PartitionTest(t)
- t.Parallel()
-
- type testCase struct {
- description string
- name string
- app basics.AppIndex
- key string
- err string
- }
-
- pp := func(tc testCase) string {
- return fmt.Sprintf("<<<%s>>> (name, app) = (%#v, %d) --should--> key = %#v (err = [%s])", tc.description, tc.name, tc.app, tc.key, tc.err)
- }
-
- var testCases = []testCase{
- // COPACETIC:
- {"zero appid", "stranger", 0, "bx:\x00\x00\x00\x00\x00\x00\x00\x00stranger", ""},
- {"typical", "348-8uj", 131231, "bx:\x00\x00\x00\x00\x00\x02\x00\x9f348-8uj", ""},
- {"empty box name", "", 42, "bx:\x00\x00\x00\x00\x00\x00\x00*", ""},
- {"random byteslice", "{\xbb\x04\a\xd1\xe2\xc6I\x81{", 13475904583033571713, "bx:\xbb\x04\a\xd1\xe2\xc6I\x81{\xbb\x04\a\xd1\xe2\xc6I\x81{", ""},
-
- // ERRORS:
- {"too short", "", 0, "stranger", "SplitBoxKey() cannot extract AppIndex as key (stranger) too short (length=8)"},
- {"wrong prefix", "", 0, "strangersINTHEdark", "SplitBoxKey() illegal app box prefix in key (strangersINTHEdark). Expected prefix 'bx:'"},
- }
-
- for _, tc := range testCases {
- app, name, err := logic.SplitBoxKey(tc.key)
-
- if tc.err == "" {
- key := logic.MakeBoxKey(tc.app, tc.name)
- require.Equal(t, tc.app, app, pp(tc))
- require.Equal(t, tc.name, name, pp(tc))
- require.Equal(t, tc.key, key, pp(tc))
- } else {
- require.EqualError(t, err, tc.err, pp(tc))
- }
- }
-}
diff --git a/data/transactions/logic/debugger.go b/data/transactions/logic/debugger.go
index a2a0453c4..0a8e013da 100644
--- a/data/transactions/logic/debugger.go
+++ b/data/transactions/logic/debugger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -33,19 +33,80 @@ import (
"github.com/algorand/go-algorand/protocol"
)
-// DebuggerHook functions are called by eval function during TEAL program execution
-// if provided
-type DebuggerHook interface {
+// Debugger is an interface that supports the first version of AVM debuggers.
+// It consists of a set of functions called by eval function during AVM program execution.
+//
+// Deprecated: This interface does not support non-app call or inner transactions. Use EvalTracer
+// instead.
+type Debugger interface {
// Register is fired on program creation
- Register(state *DebugState) error
+ Register(state *DebugState)
// Update is fired on every step
- Update(state *DebugState) error
+ Update(state *DebugState)
// Complete is called when the program exits
- Complete(state *DebugState) error
+ Complete(state *DebugState)
+}
+
+type debuggerEvalTracerAdaptor struct {
+ NullEvalTracer
+
+ debugger Debugger
+ txnDepth int
+ debugState *DebugState
+}
+
+// MakeEvalTracerDebuggerAdaptor creates an adaptor that externally adheres to the EvalTracer
+// interface, but drives a Debugger interface
+//
+// Warning: The output EvalTracer is specifically designed to be invoked under the exact same
+// circumstances that the previous Debugger interface was invoked. This means that it will only work
+// properly if you attach it directly to a logic.EvalParams and execute a program. If you attempt to
+// run this EvalTracer under a different entry point (such as by attaching it to a BlockEvaluator),
+// it WILL NOT work properly.
+func MakeEvalTracerDebuggerAdaptor(debugger Debugger) EvalTracer {
+ return &debuggerEvalTracerAdaptor{debugger: debugger}
+}
+
+// BeforeTxnGroup updates inner txn depth
+func (a *debuggerEvalTracerAdaptor) BeforeTxnGroup(ep *EvalParams) {
+ a.txnDepth++
+}
+
+// AfterTxnGroup updates inner txn depth
+func (a *debuggerEvalTracerAdaptor) AfterTxnGroup(ep *EvalParams) {
+ a.txnDepth--
+}
+
+// BeforeProgram invokes the debugger's Register hook
+func (a *debuggerEvalTracerAdaptor) BeforeProgram(cx *EvalContext) {
+ if a.txnDepth > 0 {
+ // only report updates for top-level transactions, for backwards compatibility
+ return
+ }
+ a.debugState = makeDebugState(cx)
+ a.debugger.Register(a.refreshDebugState(cx, nil))
+}
+
+// BeforeOpcode invokes the debugger's Update hook
+func (a *debuggerEvalTracerAdaptor) BeforeOpcode(cx *EvalContext) {
+ if a.txnDepth > 0 {
+ // only report updates for top-level transactions, for backwards compatibility
+ return
+ }
+ a.debugger.Update(a.refreshDebugState(cx, nil))
+}
+
+// AfterProgram invokes the debugger's Complete hook
+func (a *debuggerEvalTracerAdaptor) AfterProgram(cx *EvalContext, evalError error) {
+ if a.txnDepth > 0 {
+ // only report updates for top-level transactions, for backwards compatibility
+ return
+ }
+ a.debugger.Complete(a.refreshDebugState(cx, evalError))
}
-// WebDebuggerHook represents a connection to tealdbg
-type WebDebuggerHook struct {
+// WebDebugger represents a connection to tealdbg
+type WebDebugger struct {
URL string
}
@@ -115,7 +176,7 @@ func makeDebugState(cx *EvalContext) *DebugState {
globals := make([]basics.TealValue, len(globalFieldSpecs))
for _, fs := range globalFieldSpecs {
// Don't try to grab app only fields when evaluating a signature
- if (cx.runModeFlags&modeSig) != 0 && fs.mode == modeApp {
+ if (cx.runModeFlags&ModeSig) != 0 && fs.mode == ModeApp {
continue
}
sv, err := cx.globalFieldToValue(fs)
@@ -126,7 +187,7 @@ func makeDebugState(cx *EvalContext) *DebugState {
}
ds.Globals = globals
- if (cx.runModeFlags & modeApp) != 0 {
+ if (cx.runModeFlags & ModeApp) != 0 {
ds.EvalDelta = cx.txn.EvalDelta
}
@@ -221,8 +282,8 @@ func (d *DebugState) parseCallstack(callstack []frame) []CallFrame {
return callFrames
}
-func (cx *EvalContext) refreshDebugState(evalError error) *DebugState {
- ds := cx.debugState
+func (a *debuggerEvalTracerAdaptor) refreshDebugState(cx *EvalContext, evalError error) *DebugState {
+ ds := a.debugState
// Update pc, line, error, stack, scratch space, callstack,
// and opcode budget
@@ -247,14 +308,14 @@ func (cx *EvalContext) refreshDebugState(evalError error) *DebugState {
ds.OpcodeBudget = cx.remainingBudget()
ds.CallStack = ds.parseCallstack(cx.callstack)
- if (cx.runModeFlags & modeApp) != 0 {
+ if (cx.runModeFlags & ModeApp) != 0 {
ds.EvalDelta = cx.txn.EvalDelta
}
return ds
}
-func (dbg *WebDebuggerHook) postState(state *DebugState, endpoint string) error {
+func (dbg *WebDebugger) postState(state *DebugState, endpoint string) error {
var body bytes.Buffer
enc := protocol.NewJSONEncoder(&body)
err := enc.Encode(state)
@@ -285,7 +346,7 @@ func (dbg *WebDebuggerHook) postState(state *DebugState, endpoint string) error
}
// Register sends state to remote debugger
-func (dbg *WebDebuggerHook) Register(state *DebugState) error {
+func (dbg *WebDebugger) Register(state *DebugState) {
u, err := url.Parse(dbg.URL)
if err != nil {
logging.Base().Errorf("Failed to parse url: %s", err.Error())
@@ -295,15 +356,24 @@ func (dbg *WebDebuggerHook) Register(state *DebugState) error {
if h != "localhost" && h != "127.0.0.1" && h != "::1" {
logging.Base().Warnf("Unsecured communication with non-local debugger: %s", h)
}
- return dbg.postState(state, "exec/register")
+ err = dbg.postState(state, "exec/register")
+ if err != nil {
+ logging.Base().Errorf("Failed to post state to exec/register: %s", err.Error())
+ }
}
// Update sends state to remote debugger
-func (dbg *WebDebuggerHook) Update(state *DebugState) error {
- return dbg.postState(state, "exec/update")
+func (dbg *WebDebugger) Update(state *DebugState) {
+ err := dbg.postState(state, "exec/update")
+ if err != nil {
+ logging.Base().Errorf("Failed to post state to exec/update: %s", err.Error())
+ }
}
// Complete sends state to remote debugger
-func (dbg *WebDebuggerHook) Complete(state *DebugState) error {
- return dbg.postState(state, "exec/complete")
+func (dbg *WebDebugger) Complete(state *DebugState) {
+ err := dbg.postState(state, "exec/complete")
+ if err != nil {
+ logging.Base().Errorf("Failed to post state to exec/complete: %s", err.Error())
+ }
}
diff --git a/data/transactions/logic/debugger_test.go b/data/transactions/logic/debugger_test.go
index 2775e74e2..26bd940b7 100644
--- a/data/transactions/logic/debugger_test.go
+++ b/data/transactions/logic/debugger_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -26,7 +26,7 @@ import (
"github.com/stretchr/testify/require"
)
-var testProgram string = `intcblock 0 1 1 1 1 5 100
+const debuggerTestProgram string = `intcblock 0 1 1 1 1 5 100
bytecblock 0x414c474f 0x1337 0x2001 0xdeadbeef 0x70077007
bytec 0
sha256
@@ -63,7 +63,7 @@ bytec 4
&&
`
-func TestWebDebuggerManual(t *testing.T) {
+func TestWebDebuggerManual(t *testing.T) { //nolint:paralleltest // Manual test
partitiontest.PartitionTest(t)
debugURL := os.Getenv("TEAL_DEBUGGER_URL")
@@ -80,51 +80,84 @@ func TestWebDebuggerManual(t *testing.T) {
tx.SelectionPK[:],
tx.Note,
}
- ep.Debugger = &WebDebuggerHook{URL: debugURL}
- testLogic(t, testProgram, AssemblerMaxVersion, ep)
+ ep.Tracer = MakeEvalTracerDebuggerAdaptor(&WebDebugger{URL: debugURL})
+ testLogic(t, debuggerTestProgram, AssemblerMaxVersion, ep)
}
-type testDbgHook struct {
+type testDebugger struct {
register int
update int
complete int
state *DebugState
}
-func (d *testDbgHook) Register(state *DebugState) error {
+func (d *testDebugger) Register(state *DebugState) {
d.register++
d.state = state
- return nil
}
-func (d *testDbgHook) Update(state *DebugState) error {
+func (d *testDebugger) Update(state *DebugState) {
d.update++
d.state = state
- return nil
}
-func (d *testDbgHook) Complete(state *DebugState) error {
+func (d *testDebugger) Complete(state *DebugState) {
d.complete++
d.state = state
- return nil
}
-func TestDebuggerHook(t *testing.T) {
+func TestDebuggerProgramEval(t *testing.T) {
partitiontest.PartitionTest(t)
-
- testDbg := testDbgHook{}
- ep := defaultEvalParams()
- ep.Debugger = &testDbg
- testLogic(t, testProgram, AssemblerMaxVersion, ep)
-
- require.Equal(t, 1, testDbg.register)
- require.Equal(t, 1, testDbg.complete)
- require.Greater(t, testDbg.update, 1)
- require.Len(t, testDbg.state.Stack, 1)
+ t.Parallel()
+
+ t.Run("logicsig", func(t *testing.T) {
+ t.Parallel()
+ testDbg := testDebugger{}
+ ep := defaultEvalParams()
+ ep.Tracer = MakeEvalTracerDebuggerAdaptor(&testDbg)
+ testLogic(t, debuggerTestProgram, AssemblerMaxVersion, ep)
+
+ require.Equal(t, 1, testDbg.register)
+ require.Equal(t, 1, testDbg.complete)
+ require.Equal(t, 35, testDbg.update)
+ require.Len(t, testDbg.state.Stack, 1)
+ })
+
+ t.Run("simple app", func(t *testing.T) {
+ t.Parallel()
+ testDbg := testDebugger{}
+ ep := defaultEvalParams()
+ ep.Tracer = MakeEvalTracerDebuggerAdaptor(&testDbg)
+ testApp(t, debuggerTestProgram, ep)
+
+ require.Equal(t, 1, testDbg.register)
+ require.Equal(t, 1, testDbg.complete)
+ require.Equal(t, 35, testDbg.update)
+ require.Len(t, testDbg.state.Stack, 1)
+ })
+
+ t.Run("app with inner txns", func(t *testing.T) {
+ t.Parallel()
+ testDbg := testDebugger{}
+ ep, tx, ledger := MakeSampleEnv()
+
+ // Establish 888 as the app id, and fund it.
+ ledger.NewApp(tx.Receiver, 888, basics.AppParams{})
+ ledger.NewAccount(basics.AppIndex(888).Address(), 200000)
+
+ ep.Tracer = MakeEvalTracerDebuggerAdaptor(&testDbg)
+ testApp(t, innerTxnTestProgram, ep)
+
+ require.Equal(t, 1, testDbg.register)
+ require.Equal(t, 1, testDbg.complete)
+ require.Equal(t, 27, testDbg.update)
+ require.Len(t, testDbg.state.Stack, 1)
+ })
}
func TestLineToPC(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
dState := DebugState{
Disassembly: "abc\ndef\nghi",
@@ -162,6 +195,7 @@ func TestLineToPC(t *testing.T) {
func TestValueDeltaToValueDelta(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
vDelta := basics.ValueDelta{
Action: basics.SetUintAction,
@@ -186,6 +220,7 @@ intc_0
func TestParseCallstack(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
expectedCallFrames := []CallFrame{
{
@@ -210,6 +245,7 @@ func TestParseCallstack(t *testing.T) {
func TestCallStackUpdate(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
expectedCallFrames := []CallFrame{
{
@@ -222,9 +258,9 @@ func TestCallStackUpdate(t *testing.T) {
},
}
- testDbg := testDbgHook{}
+ testDbg := testDebugger{}
ep := defaultEvalParams()
- ep.Debugger = &testDbg
+ ep.Tracer = MakeEvalTracerDebuggerAdaptor(&testDbg)
testLogic(t, testCallStackProgram, AssemblerMaxVersion, ep)
require.Equal(t, 1, testDbg.register)
diff --git a/data/transactions/logic/doc.go b/data/transactions/logic/doc.go
index 243c22ec2..31a0412fa 100644
--- a/data/transactions/logic/doc.go
+++ b/data/transactions/logic/doc.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -224,12 +224,12 @@ var opcodeImmediateNotes = map[string]string{
"intc": "{uint8 int constant index}",
"pushint": "{varuint int}",
"pushints": "{varuint count} [{varuint value}, ...]",
- "bytecblock": "{varuint count} [({varuint value length} bytes), ...]",
+ "bytecblock": "{varuint count} [({varuint length} bytes), ...]",
"bytec": "{uint8 byte constant index}",
"pushbytes": "{varuint length} {bytes}",
- "pushbytess": "{varuint count} [({varuint value length} bytes), ...]",
+ "pushbytess": "{varuint count} [({varuint length} bytes), ...]",
- "arg": "{uint8 arg index N}",
+ "arg": "{uint8 arg index}",
"global": "{uint8 global field index}",
"txn": "{uint8 transaction field index}",
@@ -279,10 +279,10 @@ var opcodeImmediateNotes = map[string]string{
"ecdsa_pk_recover": "{uint8 curve index}",
"base64_decode": "{uint8 encoding index}",
- "json_ref": "{uint8 return type}",
+ "json_ref": "{uint8 return type index}",
"vrf_verify": "{uint8 parameters index}",
- "block": "{uint8 block field}",
+ "block": "{uint8 block field index}",
"switch": "{uint8 branch count} [{int16 branch offset, big-endian}, ...]",
"match": "{uint8 branch count} [{int16 branch offset, big-endian}, ...]",
diff --git a/data/transactions/logic/doc_test.go b/data/transactions/logic/doc_test.go
index 9b5f2a950..8afca4520 100644
--- a/data/transactions/logic/doc_test.go
+++ b/data/transactions/logic/doc_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -27,6 +27,7 @@ import (
func TestOpDocs(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
opsSeen := make(map[string]bool, len(OpSpecs))
for _, op := range OpSpecs {
@@ -50,6 +51,7 @@ func TestOpDocs(t *testing.T) {
// around for non-existent opcodes, most likely from a rename.
func TestDocStragglers(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
for op := range opDocExtras {
_, ok := opDocByName[op]
@@ -63,6 +65,7 @@ func TestDocStragglers(t *testing.T) {
func TestOpGroupCoverage(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
opsSeen := make(map[string]bool, len(OpSpecs))
for _, op := range OpSpecs {
@@ -87,6 +90,7 @@ func TestOpGroupCoverage(t *testing.T) {
func TestOpDoc(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
xd := OpDoc("txn")
require.NotEmpty(t, xd)
@@ -96,6 +100,7 @@ func TestOpDoc(t *testing.T) {
func TestOpImmediateNote(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
xd := OpImmediateNote("txn")
require.NotEmpty(t, xd)
@@ -105,6 +110,7 @@ func TestOpImmediateNote(t *testing.T) {
func TestAllImmediatesDocumented(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
for _, op := range OpSpecs {
count := len(op.Immediates)
@@ -135,6 +141,7 @@ func TestAllImmediatesDocumented(t *testing.T) {
func TestOpDocExtra(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
xd := OpDocExtra("bnz")
require.NotEmpty(t, xd)
@@ -144,6 +151,7 @@ func TestOpDocExtra(t *testing.T) {
func TestOpAllCosts(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
a := OpAllCosts("+")
require.Len(t, a, 1)
@@ -158,6 +166,7 @@ func TestOpAllCosts(t *testing.T) {
func TestOnCompletionDescription(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
desc := OnCompletionDescription(0)
require.Equal(t, "Only execute the `ApprovalProgram` associated with this application ID, with no additional effects.", desc)
diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go
index ab03e0f95..158ca6e08 100644
--- a/data/transactions/logic/eval.go
+++ b/data/transactions/logic/eval.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -282,8 +282,8 @@ type EvalParams struct {
SigLedger LedgerForSignature
Ledger LedgerForLogic
- // optional debugger
- Debugger DebuggerHook
+ // optional tracer
+ Tracer EvalTracer
// MinAvmVersion is the minimum allowed AVM version of this program.
// The program must reject if its version is less than this version. If
@@ -455,7 +455,7 @@ func NewInnerEvalParams(txg []transactions.SignedTxnWithAD, caller *EvalContext)
logger: caller.logger,
SigLedger: caller.SigLedger,
Ledger: caller.Ledger,
- Debugger: nil, // See #4438, where this becomes caller.Debugger
+ Tracer: caller.Tracer,
MinAvmVersion: &minAvmVersion,
FeeCredit: caller.FeeCredit,
Specials: caller.Specials,
@@ -474,28 +474,31 @@ func NewInnerEvalParams(txg []transactions.SignedTxnWithAD, caller *EvalContext)
type evalFunc func(cx *EvalContext) error
type checkFunc func(cx *EvalContext) error
-type runMode uint64
+// RunMode is a bitset of logic evaluation modes.
+// There are currently two such modes: Signature and Application.
+type RunMode uint64
const (
- // modeSig is LogicSig execution
- modeSig runMode = 1 << iota
+ // ModeSig is LogicSig execution
+ ModeSig RunMode = 1 << iota
- // modeApp is application/contract execution
- modeApp
+ // ModeApp is application/contract execution
+ ModeApp
// local constant, run in any mode
- modeAny = modeSig | modeApp
+ modeAny = ModeSig | ModeApp
)
-func (r runMode) Any() bool {
+// Any checks if this mode bitset represents any evaluation mode
+func (r RunMode) Any() bool {
return r == modeAny
}
-func (r runMode) String() string {
+func (r RunMode) String() string {
switch r {
- case modeSig:
+ case ModeSig:
return "Signature"
- case modeApp:
+ case ModeApp:
return "Application"
case modeAny:
return "Any"
@@ -547,7 +550,7 @@ type EvalContext struct {
*EvalParams
// determines eval mode: runModeSignature or runModeApplication
- runModeFlags runMode
+ runModeFlags RunMode
// the index of the transaction being evaluated
groupIndex int
@@ -591,9 +594,11 @@ type EvalContext struct {
instructionStarts []bool
programHashCached crypto.Digest
+}
- // Stores state & disassembly for the optional debugger
- debugState *DebugState
+// RunMode returns the evaluation context's mode (signature or application)
+func (cx *EvalContext) RunMode() RunMode {
+ return cx.runModeFlags
}
// StackType describes the type of a value on the operand stack
@@ -700,7 +705,7 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam
}
cx := EvalContext{
EvalParams: params,
- runModeFlags: modeApp,
+ runModeFlags: ModeApp,
groupIndex: gi,
txn: &params.TxnGroup[gi],
appID: aid,
@@ -787,7 +792,7 @@ func EvalSignatureFull(gi int, params *EvalParams) (pass bool, pcx *EvalContext,
}
cx := EvalContext{
EvalParams: params,
- runModeFlags: modeSig,
+ runModeFlags: ModeSig,
groupIndex: gi,
txn: &params.TxnGroup[gi],
}
@@ -834,17 +839,11 @@ func eval(program []byte, cx *EvalContext) (pass bool, err error) {
cx.txn.EvalDelta.GlobalDelta = basics.StateDelta{}
cx.txn.EvalDelta.LocalDeltas = make(map[uint64]basics.StateDelta)
- if cx.Debugger != nil {
- cx.debugState = makeDebugState(cx)
- if derr := cx.Debugger.Register(cx.refreshDebugState(err)); derr != nil {
- return false, derr
- }
+ if cx.Tracer != nil {
+ cx.Tracer.BeforeProgram(cx)
defer func() {
- // Ensure we update the debugger before exiting
- errDbg := cx.Debugger.Complete(cx.refreshDebugState(err))
- if err == nil {
- err = errDbg
- }
+ // Ensure we update the tracer before exiting
+ cx.Tracer.AfterProgram(cx, err)
}()
}
@@ -859,13 +858,15 @@ func eval(program []byte, cx *EvalContext) (pass bool, err error) {
}
for (err == nil) && (cx.pc < len(cx.program)) {
- if cx.Debugger != nil {
- if derr := cx.Debugger.Update(cx.refreshDebugState(err)); derr != nil {
- return false, derr
- }
+ if cx.Tracer != nil {
+ cx.Tracer.BeforeOpcode(cx)
}
err = cx.step()
+
+ if cx.Tracer != nil {
+ cx.Tracer.AfterOpcode(cx, err)
+ }
}
if err != nil {
if cx.Trace != nil {
@@ -895,17 +896,17 @@ func eval(program []byte, cx *EvalContext) (pass bool, err error) {
// these static checks include a cost estimate that must be low enough
// (controlled by params.Proto).
func CheckContract(program []byte, params *EvalParams) error {
- return check(program, params, modeApp)
+ return check(program, params, ModeApp)
}
// CheckSignature should be faster than EvalSignature. It can perform static
// checks and reject programs that are invalid. Prior to v4, these static checks
// include a cost estimate that must be low enough (controlled by params.Proto).
func CheckSignature(gi int, params *EvalParams) error {
- return check(params.TxnGroup[gi].Lsig.Logic, params, modeSig)
+ return check(params.TxnGroup[gi].Lsig.Logic, params, ModeSig)
}
-func check(program []byte, params *EvalParams, mode runMode) (err error) {
+func check(program []byte, params *EvalParams, mode RunMode) (err error) {
defer func() {
if x := recover(); x != nil {
buf := make([]byte, 16*1024)
@@ -1012,7 +1013,7 @@ func (cx *EvalContext) Cost() int {
}
func (cx *EvalContext) remainingBudget() int {
- if cx.runModeFlags == modeSig {
+ if cx.runModeFlags == ModeSig {
return int(cx.Proto.LogicSigMaxCost) - cx.cost
}
@@ -1822,6 +1823,15 @@ func opBytesSqrt(cx *EvalContext) error {
return nil
}
+func nonzero(b []byte) []byte {
+ for i := range b {
+ if b[i] != 0 {
+ return b[i:]
+ }
+ }
+ return nil
+}
+
func opBytesLt(cx *EvalContext) error {
last := len(cx.stack) - 1
prev := last - 1
@@ -1830,9 +1840,18 @@ func opBytesLt(cx *EvalContext) error {
return errors.New("math attempted on large byte-array")
}
- rhs := new(big.Int).SetBytes(cx.stack[last].Bytes)
- lhs := new(big.Int).SetBytes(cx.stack[prev].Bytes)
- cx.stack[prev] = boolToSV(lhs.Cmp(rhs) < 0)
+ rhs := nonzero(cx.stack[last].Bytes)
+ lhs := nonzero(cx.stack[prev].Bytes)
+
+ switch {
+ case len(lhs) < len(rhs):
+ cx.stack[prev] = boolToSV(true)
+ case len(lhs) > len(rhs):
+ cx.stack[prev] = boolToSV(false)
+ default:
+ cx.stack[prev] = boolToSV(bytes.Compare(lhs, rhs) < 0)
+ }
+
cx.stack = cx.stack[:last]
return nil
}
@@ -1866,9 +1885,10 @@ func opBytesEq(cx *EvalContext) error {
return errors.New("math attempted on large byte-array")
}
- rhs := new(big.Int).SetBytes(cx.stack[last].Bytes)
- lhs := new(big.Int).SetBytes(cx.stack[prev].Bytes)
- cx.stack[prev] = boolToSV(lhs.Cmp(rhs) == 0)
+ rhs := nonzero(cx.stack[last].Bytes)
+ lhs := nonzero(cx.stack[prev].Bytes)
+
+ cx.stack[prev] = boolToSV(bytes.Equal(lhs, rhs))
cx.stack = cx.stack[:last]
return nil
}
@@ -2602,7 +2622,7 @@ func (cx *EvalContext) getTxID(txn *transactions.Transaction, groupIndex int, in
func (cx *EvalContext) txnFieldToStack(stxn *transactions.SignedTxnWithAD, fs *txnFieldSpec, arrayFieldIdx uint64, groupIndex int, inner bool) (sv stackValue, err error) {
if fs.effects {
- if cx.runModeFlags == modeSig {
+ if cx.runModeFlags == ModeSig {
return sv, fmt.Errorf("txn[%s] not allowed in current mode", fs.field)
}
if cx.version < txnEffectsVersion && !inner {
@@ -2870,7 +2890,7 @@ func (cx *EvalContext) opTxnImpl(gi uint64, src txnSource, field TxnField, ai ui
case srcGroup:
if fs.effects && gi >= uint64(cx.groupIndex) {
// Test mode so that error is clearer
- if cx.runModeFlags == modeSig {
+ if cx.runModeFlags == ModeSig {
return sv, fmt.Errorf("txn[%s] not allowed in current mode", fs.field)
}
return sv, fmt.Errorf("txn effects can only be read from past txns %d %d", gi, cx.groupIndex)
@@ -5154,7 +5174,16 @@ func opItxnSubmit(cx *EvalContext) error {
}
ep := NewInnerEvalParams(cx.subtxns, cx)
+
+ if ep.Tracer != nil {
+ ep.Tracer.BeforeTxnGroup(ep)
+ }
+
for i := range ep.TxnGroup {
+ if ep.Tracer != nil {
+ ep.Tracer.BeforeTxn(ep, i)
+ }
+
err := cx.Ledger.Perform(i, ep)
if err != nil {
return err
@@ -5162,11 +5191,20 @@ func opItxnSubmit(cx *EvalContext) error {
// This is mostly a no-op, because Perform does its work "in-place", but
// RecordAD has some further responsibilities.
ep.RecordAD(i, ep.TxnGroup[i].ApplyData)
+
+ if ep.Tracer != nil {
+ ep.Tracer.AfterTxn(ep, i, ep.TxnGroup[i].ApplyData)
+ }
}
cx.txn.EvalDelta.InnerTxns = append(cx.txn.EvalDelta.InnerTxns, ep.TxnGroup...)
cx.subtxns = nil
// must clear the inner txid cache, otherwise prior inner txids will be returned for this group
cx.innerTxidCache = nil
+
+ if ep.Tracer != nil {
+ ep.Tracer.AfterTxnGroup(ep)
+ }
+
return nil
}
diff --git a/data/transactions/logic/evalAppTxn_test.go b/data/transactions/logic/evalAppTxn_test.go
index 25a073ba3..b33a97d74 100644
--- a/data/transactions/logic/evalAppTxn_test.go
+++ b/data/transactions/logic/evalAppTxn_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -35,6 +35,7 @@ import (
func TestInnerTypesV5(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
v5, _, _ := MakeSampleEnvWithVersion(5)
// not alllowed in v5
@@ -47,6 +48,7 @@ func TestInnerTypesV5(t *testing.T) {
func TestCurrentInnerTypes(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
TestApp(t, "itxn_submit; int 1;", ep, "itxn_submit without itxn_begin")
@@ -99,6 +101,7 @@ func TestCurrentInnerTypes(t *testing.T) {
func TestFieldTypes(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, _, _ := MakeSampleEnv()
TestApp(t, "itxn_begin; byte \"pay\"; itxn_field Sender;", ep, "not an address")
@@ -129,6 +132,7 @@ func appAddr(id int) basics.Address {
func TestAppPay(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -176,6 +180,7 @@ func TestAppPay(t *testing.T) {
func TestAppAssetOptIn(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
// Establish 888 as the app id, and fund it.
@@ -237,6 +242,7 @@ int 1
func TestRekeyPay(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -262,6 +268,7 @@ func TestRekeyPay(t *testing.T) {
func TestRekeyBack(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
payAndUnkey := `
itxn_begin
@@ -288,6 +295,7 @@ func TestRekeyBack(t *testing.T) {
func TestDefaultSender(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -309,6 +317,7 @@ func TestDefaultSender(t *testing.T) {
func TestAppAxfer(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
axfer := `
itxn_begin
@@ -372,6 +381,7 @@ func TestAppAxfer(t *testing.T) {
func TestExtraFields(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -394,6 +404,7 @@ func TestExtraFields(t *testing.T) {
func TestBadFieldV5(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -418,6 +429,7 @@ func TestBadFieldV5(t *testing.T) {
func TestBadField(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -443,6 +455,7 @@ func TestBadField(t *testing.T) {
func TestNumInnerShallow(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -485,6 +498,7 @@ func TestNumInnerShallow(t *testing.T) {
// in a group.
func TestNumInnerPooled(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -523,6 +537,7 @@ func TestNumInnerPooled(t *testing.T) {
func TestAssetCreate(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
create := `
itxn_begin
@@ -551,6 +566,7 @@ func TestAssetCreate(t *testing.T) {
func TestAssetFreeze(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
create := `
itxn_begin
@@ -599,6 +615,7 @@ func TestAssetFreeze(t *testing.T) {
func TestKeyReg(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
keyreg := `
store 6 // StateProofPK
@@ -655,6 +672,7 @@ func TestKeyReg(t *testing.T) {
`
t.Run("nonparticipating", func(t *testing.T) {
+ t.Parallel()
params := `
int 0 // VoteFirst
int 0 // VoteLast
@@ -671,6 +689,7 @@ func TestKeyReg(t *testing.T) {
})
t.Run("offline", func(t *testing.T) {
+ t.Parallel()
params := `
int 0 // VoteFirst
int 0 // VoteLast
@@ -687,6 +706,7 @@ func TestKeyReg(t *testing.T) {
})
t.Run("online without StateProofPK", func(t *testing.T) {
+ t.Parallel()
params := `
int 100 // VoteFirst
int 200 // VoteLast
@@ -704,6 +724,7 @@ func TestKeyReg(t *testing.T) {
})
t.Run("online with StateProofPK", func(t *testing.T) {
+ t.Parallel()
params := `
int 100 // VoteFirst
int 16777315 // VoteLast
@@ -722,6 +743,7 @@ func TestKeyReg(t *testing.T) {
})
t.Run("online with StateProofPK and too long validity period", func(t *testing.T) {
+ t.Parallel()
params := `
int 100 // VoteFirst
int 16777316 // VoteLast
@@ -742,6 +764,7 @@ func TestKeyReg(t *testing.T) {
func TestFieldSetting(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
ledger.NewApp(tx.Receiver, 888, basics.AppParams{})
@@ -786,6 +809,7 @@ func TestFieldSetting(t *testing.T) {
func TestInnerGroup(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
ep.FeeCredit = nil // default sample env starts at 401
@@ -808,6 +832,7 @@ txn Sender; itxn_field Receiver;
func TestInnerFeePooling(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
ep.FeeCredit = nil // default sample env starts at 401
@@ -878,6 +903,7 @@ txn Sender; itxn_field Receiver;
// immediate failures.
func TestApplCreation(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, _ := MakeSampleEnv()
@@ -963,8 +989,6 @@ func TestBigApplCreation(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- ep, _, _ := MakeSampleEnv()
-
p := "itxn_begin;"
s := "; int 1"
@@ -974,7 +998,12 @@ func TestBigApplCreation(t *testing.T) {
// First, test normal accummulation
for _, pgm := range []string{"Approval", "ClearState"} {
+ pgm := pgm
t.Run(pgm, func(t *testing.T) {
+ t.Parallel()
+
+ ep, _, _ := MakeSampleEnv()
+
basic := "itxn_field " + pgm + "Program"
pages := "itxn_field " + pgm + "ProgramPages"
TestApp(t, p+`int 1000; bzero; `+pages+`
@@ -1062,6 +1091,7 @@ func TestApplSubmission(t *testing.T) {
func TestInnerApplCreate(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
ledger.NewApp(tx.Receiver, 888, basics.AppParams{})
@@ -1129,6 +1159,7 @@ int 5000; app_params_get AppGlobalNumByteSlice; !; assert; !; assert; int 1
func TestCreateOldAppFails(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
ledger.NewApp(tx.Receiver, 888, basics.AppParams{})
@@ -1191,6 +1222,7 @@ int 1
func TestSelfReentrancy(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
ledger.NewApp(tx.Receiver, 888, basics.AppParams{})
@@ -1207,6 +1239,7 @@ int 1
func TestIndirectReentrancy(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
call888 := TestProg(t, `itxn_begin
@@ -1236,6 +1269,7 @@ int 1
// needlessly picky to test, but the appID used to be stored outside the cx.
func TestInnerAppID(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
logID := TestProg(t, `global CurrentApplicationID; itob; log; int 1`, AssemblerMaxVersion)
@@ -1264,6 +1298,7 @@ int 222
// about 690 (see next test).
func TestInnerBudgetIncrement(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
gasup := TestProg(t, "pushint 1", AssemblerMaxVersion)
@@ -1293,6 +1328,7 @@ itxn_submit;
func TestIncrementCheck(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
gasup := TestProg(t, "pushint 1", AssemblerMaxVersion)
@@ -1326,6 +1362,7 @@ int 1
// TestInnerTxIDs confirms that TxIDs are available and different
func TestInnerTxIDs(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
txid := TestProg(t, "txn TxID; log; int 1", AssemblerMaxVersion)
@@ -1421,6 +1458,7 @@ gitxn 1 TxID
// but set and unique on non-singletons
func TestInnerGroupIDs(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
gid := TestProg(t, "global GroupID; log; int 1", AssemblerMaxVersion)
@@ -1761,7 +1799,9 @@ int 1
`
for _, unified := range []bool{true, false} {
- t.Run(fmt.Sprintf("unified=%t", unified), func(t *testing.T) { //nolint:paralleltest // NO t.Parallel(). unified variable is actually shared
+ unified := unified
+ t.Run(fmt.Sprintf("unified=%t", unified), func(t *testing.T) {
+ t.Parallel()
ep, parentTx, ledger := MakeSampleEnv()
ep.Proto.UnifyInnerTxIDs = unified
@@ -2087,9 +2127,9 @@ int 1
`
for _, unified := range []bool{true, false} {
+ unified := unified
t.Run(fmt.Sprintf("unified=%t", unified), func(t *testing.T) {
- // t.Parallel() NO! unified variable is actually shared
-
+ t.Parallel()
ep, parentTx, ledger := MakeSampleEnv()
ep.Proto.UnifyInnerTxIDs = unified
@@ -2224,8 +2264,10 @@ func TestInnerTxIDCaching(t *testing.T) {
parentAppID := basics.AppIndex(888)
childAppID := basics.AppIndex(222)
- for _, unified := range []bool{true, false} { //nolint:paralleltest // NO t.Parallel(). unified variable is actually shared
+ for _, unified := range []bool{true, false} {
+ unified := unified
t.Run(fmt.Sprintf("unified=%t", unified), func(t *testing.T) {
+ t.Parallel()
ep, parentTx, ledger := MakeSampleEnv()
ep.Proto.UnifyInnerTxIDs = unified
@@ -2321,6 +2363,7 @@ btoi
// TestGtixn confirms access to itxn groups
func TestGtixn(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
two := TestProg(t, "byte 0x22; log; int 1", AssemblerMaxVersion)
@@ -2407,6 +2450,7 @@ int 1
// TestGtxnLog confirms that gtxn can now access previous txn's Logs.
func TestGtxnLog(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
two := TestProg(t, "byte 0x22; log; int 1", AssemblerMaxVersion)
@@ -2436,6 +2480,7 @@ int 1
// TestGtxnApps confirms that gtxn can now access previous txn's created app id.
func TestGtxnApps(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
appcheck := TestProg(t, `
@@ -2483,6 +2528,7 @@ int 5001
// TestGtxnAsa confirms that gtxn can now access previous txn's created asa id.
func TestGtxnAsa(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
appcheck := TestProg(t, `
@@ -2520,6 +2566,7 @@ int 5001
// TestCallerGlobals checks that a called app can see its caller.
func TestCallerGlobals(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
globals := TestProg(t, fmt.Sprintf(`
@@ -2550,6 +2597,7 @@ int 1
// transactions.
func TestNumInnerDeep(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay := `
itxn_begin
@@ -2600,6 +2648,7 @@ itxn_submit
// foreign-arrays rules.
func TestCreateAndUse(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
axfer := `
itxn_begin
@@ -2715,6 +2764,7 @@ func hexProgram(t *testing.T, source string) string {
// the address for it can be looked up.
func TestCreateUseApp(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay5back := main(`
itxn_begin
@@ -2749,6 +2799,7 @@ int 1
// because of the strict adherence to the foreign-accounts rules.
func TestCreateAndPay(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
pay5back := main(`
itxn_begin
@@ -2792,6 +2843,7 @@ int 1
// across multiple inner transaction groups
func TestInnerGaid(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
ep.Proto.MaxInnerTransactions = 100
@@ -2983,6 +3035,7 @@ itxn_submit
func TestForeignAppAccountAccess(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := MakeSampleEnv()
ledger.NewAccount(appAddr(888), 50_000)
diff --git a/data/transactions/logic/evalBench_test.go b/data/transactions/logic/evalBench_test.go
index 956e1441e..240da71bd 100644
--- a/data/transactions/logic/evalBench_test.go
+++ b/data/transactions/logic/evalBench_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/evalCrypto_test.go b/data/transactions/logic/evalCrypto_test.go
index e3dfff2f7..65f0787a9 100644
--- a/data/transactions/logic/evalCrypto_test.go
+++ b/data/transactions/logic/evalCrypto_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -147,19 +147,28 @@ func BenchmarkVerify(b *testing.B) {
benches := [][]string{
{"pop", "", "int 1234576; int 6712; pop; pop", "int 1"},
{"add", "", "int 1234576; int 6712; +; pop", "int 1"},
- /*
- {"ed25519verify_bare", "", `byte 0x
- byte 0x
- addr
- ed25519verify_bare
- assert`, "int 1"},*/
- {"ecdsa_verify", "", `byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f
+ {"ed25519verify_bare", "", `
+byte 0x62fdfc072182654f163f5f0f9a621d729566c74d0aa413bf009c9800418c19cd
+byte 0xaab40a8b4f1f386504af2473804abbc03bbd94506e8e0c8db881fc2b2c3aee65b867b25caa47fa25ae2105bf1731398df336213707f2d25f9b1d31b3dc133307;
+addr C7ZCK6N2AJQMVEP4FRTK2UW45UFR6DKPRJHJVWB5O4VQOZMFPK2KCMR7M4
+ed25519verify_bare; assert
+`, "int 1"},
+ {"ecdsa_verify k1", "", `
+byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f
byte 0x5eb27782eb1a5df8de9a5d51613ad5ca730840ddf4af919c6feb15cde14f9978
byte 0x0cb3c0d636ed991ee030d09c295de3121eb166cb9e1552cf0ef0fb2358f35f0f
byte 0x79de0699673571df1de8486718d06a3e7838f6831ec4ef3fb963788fbfb773b7
byte 0xd76446a3393af3e2eefada16df80cc6a881a56f4cf41fa2ab4769c5708ce878d
ecdsa_verify Secp256k1
assert`, "int 1"},
+ {"ecdsa_verify r1", "", `
+byte 0x71a5910445820f57989c027bdf9391c80097874d249e0f38bf90834fdec2877f
+byte 0xc010fc83ea196d6f5ce8a44637060bdcfb5bf1199cfc5bb893684d450c4f160c
+byte 0x8e391a7b9cd75a99e8ebfe703036caebd9e91ae8339bd7e2abfb0f273eb8e972
+byte 0x13e49a19378bbfa8d55ac81a35b87d7bae456c79fcf04a78803d8eb45b253fab
+byte 0xa2d237cd897ca70787abf04d2155c6dc2fbe26fd642e0472cd75c13dc919ef1a
+ecdsa_verify Secp256r1
+assert`, "int 1"},
{"vrf_verify", "", `byte 0x72
byte 0xae5b66bdf04b4c010bfe32b2fc126ead2107b697634f6f7337b9bff8785ee111200095ece87dde4dbe87343f6df3b107d91798c8a7eb1245d3bb9c5aafb093358c13e6ae1111a55717e895fd15f99f07
byte 0x3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
@@ -337,7 +346,9 @@ byte 0x%s
{pkTampered2, false},
}
for i, test := range decompressTests {
+ i, test, source := i, test, source
t.Run(fmt.Sprintf("decompress/pass=%v", test.pass), func(t *testing.T) {
+ t.Parallel()
t.Log("decompressTests i", i)
src := fmt.Sprintf(source, hex.EncodeToString(test.key), hex.EncodeToString(x), hex.EncodeToString(y))
if test.pass {
@@ -381,7 +392,9 @@ ecdsa_verify Secp256k1
{"testdata1", r, false},
}
for _, test := range verifyTests {
+ test, source := test, source
t.Run(fmt.Sprintf("verify/pass=%v", test.pass), func(t *testing.T) {
+ t.Parallel()
src := fmt.Sprintf(source, test.data, hex.EncodeToString(test.r), hex.EncodeToString(s), hex.EncodeToString(x), hex.EncodeToString(y))
if test.pass {
testAccepts(t, src, 5)
@@ -428,7 +441,9 @@ load 1
pkExpanded := secp256k1.S256().Marshal(key.PublicKey.X, key.PublicKey.Y)
for i, test := range recoverTests {
+ i, test, source := i, test, source
t.Run(fmt.Sprintf("recover/%d", i), func(t *testing.T) {
+ t.Parallel()
src := fmt.Sprintf(source, hex.EncodeToString(msg[:]), test.v, hex.EncodeToString(r), hex.EncodeToString(s), hex.EncodeToString(x), hex.EncodeToString(y), hex.EncodeToString(pkExpanded))
test.checker(t, src, 5)
})
@@ -487,7 +502,9 @@ byte 0x%s
{pkTampered2, false},
}
for i, test := range decompressTests {
+ i, test, source := i, test, source
t.Run(fmt.Sprintf("decompress/pass=%v", test.pass), func(t *testing.T) {
+ t.Parallel()
t.Log("decompressTests i", i)
src := fmt.Sprintf(source, hex.EncodeToString(test.key), hex.EncodeToString(x), hex.EncodeToString(y))
if test.pass {
@@ -530,7 +547,9 @@ ecdsa_verify Secp256r1
{"testdata1", r, false},
}
for _, test := range verifyTests {
+ test, source := test, source
t.Run(fmt.Sprintf("verify/pass=%v", test.pass), func(t *testing.T) {
+ t.Parallel()
src := fmt.Sprintf(source, test.data, hex.EncodeToString(test.r), hex.EncodeToString(s), hex.EncodeToString(x), hex.EncodeToString(y))
if test.pass {
testAccepts(t, src, fidoVersion)
diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go
index 0d0676c16..a2f33cc90 100644
--- a/data/transactions/logic/evalStateful_test.go
+++ b/data/transactions/logic/evalStateful_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -197,44 +197,47 @@ pop
bytec_0
log
`
- tests := map[runMode]string{
- modeSig: opcodesRunModeAny + opcodesRunModeSignature,
- modeApp: opcodesRunModeAny + opcodesRunModeApplication,
+ tests := map[RunMode]string{
+ ModeSig: opcodesRunModeAny + opcodesRunModeSignature,
+ ModeApp: opcodesRunModeAny + opcodesRunModeApplication,
}
- ep, tx, ledger := makeSampleEnv()
- ep.TxnGroup[0].Lsig.Args = [][]byte{
- tx.Sender[:],
- tx.Receiver[:],
- tx.CloseRemainderTo[:],
- tx.VotePK[:],
- tx.SelectionPK[:],
- tx.Note,
- }
- params := basics.AssetParams{
- Total: 1000,
- Decimals: 2,
- DefaultFrozen: false,
- UnitName: "ALGO",
- AssetName: "",
- URL: string(protocol.PaymentTx),
- Manager: tx.Sender,
- Reserve: tx.Receiver,
- Freeze: tx.Receiver,
- Clawback: tx.Receiver,
- }
- algoValue := basics.TealValue{Type: basics.TealUintType, Uint: 0x77}
- ledger.NewAccount(tx.Sender, 1)
- ledger.NewApp(tx.Sender, 100, basics.AppParams{})
- ledger.NewLocals(tx.Sender, 100)
- ledger.NewLocal(tx.Sender, 100, "ALGO", algoValue)
- ledger.NewAsset(tx.Sender, 5, params)
-
for mode, test := range tests {
+ mode, test := mode, test
t.Run(fmt.Sprintf("opcodes_mode=%d", mode), func(t *testing.T) {
+ t.Parallel()
+
+ ep, tx, ledger := makeSampleEnv()
+ ep.TxnGroup[0].Lsig.Args = [][]byte{
+ tx.Sender[:],
+ tx.Receiver[:],
+ tx.CloseRemainderTo[:],
+ tx.VotePK[:],
+ tx.SelectionPK[:],
+ tx.Note,
+ }
ep.TxnGroup[0].Txn.ApplicationID = 100
ep.TxnGroup[0].Txn.ForeignAssets = []basics.AssetIndex{5} // needed since v4
- if mode == modeSig {
+ params := basics.AssetParams{
+ Total: 1000,
+ Decimals: 2,
+ DefaultFrozen: false,
+ UnitName: "ALGO",
+ AssetName: "",
+ URL: string(protocol.PaymentTx),
+ Manager: tx.Sender,
+ Reserve: tx.Receiver,
+ Freeze: tx.Receiver,
+ Clawback: tx.Receiver,
+ }
+ algoValue := basics.TealValue{Type: basics.TealUintType, Uint: 0x77}
+ ledger.NewAccount(tx.Sender, 1)
+ ledger.NewApp(tx.Sender, 100, basics.AppParams{})
+ ledger.NewLocals(tx.Sender, 100)
+ ledger.NewLocal(tx.Sender, 100, "ALGO", algoValue)
+ ledger.NewAsset(tx.Sender, 5, params)
+
+ if mode == ModeSig {
testLogic(t, test, AssemblerMaxVersion, ep)
} else {
testApp(t, test, ep)
@@ -295,13 +298,14 @@ log
}
for _, source := range statefulOpcodeCalls {
+ source := source
testLogic(t, source, AssemblerMaxVersion, defaultEvalParams(),
"not allowed in current mode", "not allowed in current mode")
}
- require.Equal(t, runMode(1), modeSig)
- require.Equal(t, runMode(2), modeApp)
- require.True(t, modeAny == modeSig|modeApp)
+ require.Equal(t, RunMode(1), ModeSig)
+ require.Equal(t, RunMode(2), ModeApp)
+ require.True(t, modeAny == ModeSig|ModeApp)
require.True(t, modeAny.Any())
}
@@ -1276,7 +1280,9 @@ intc_1
"delete": {sourceDelete, 12},
}
for name, cmdtest := range tests {
+ name, cmdtest := name, cmdtest
t.Run(fmt.Sprintf("test=%s", name), func(t *testing.T) {
+ t.Parallel()
source := cmdtest.source
firstCmdOffset := cmdtest.accNumOffset
@@ -1628,7 +1634,9 @@ int 1
"delete": sourceDelete,
}
for name, source := range tests {
+ name, source := name, source
t.Run(fmt.Sprintf("test=%s", name), func(t *testing.T) {
+ t.Parallel()
ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion)
require.NoError(t, err)
@@ -2255,9 +2263,8 @@ int 1
require.Equal(t, 1, len(delta.LocalDeltas[0]))
}
-func TestEnumFieldErrors(t *testing.T) {
+func TestEnumFieldErrors(t *testing.T) { // nolint:paralleltest // manipulates globalFieldSpecs
partitiontest.PartitionTest(t)
- // t.Parallel() NO! manipulates globalFieldSpecs
source := `txn Amount`
origSpec := txnFieldSpecs[Amount]
@@ -2340,48 +2347,6 @@ func TestReturnTypes(t *testing.T) {
StackAny: "int 1\n",
StackBytes: "byte 0x33343536\n", // Which is the string "3456"
}
- ep, tx, ledger := makeSampleEnv()
-
- tx.Type = protocol.ApplicationCallTx
- tx.ApplicationID = 1
- tx.ForeignApps = []basics.AppIndex{tx.ApplicationID}
- tx.ForeignAssets = []basics.AssetIndex{basics.AssetIndex(1), basics.AssetIndex(1)}
- tx.Boxes = []transactions.BoxRef{{
- Name: []byte("3456"),
- }}
- ep.TxnGroup[0].Lsig.Args = [][]byte{
- []byte("aoeu"),
- []byte("aoeu"),
- []byte("aoeu2"),
- []byte("aoeu3"),
- }
- // We are going to run with GroupIndex=1, so make tx1 interesting too (so
- // txn can look at things)
- ep.TxnGroup[1] = ep.TxnGroup[0]
-
- ep.pastScratch[0] = &scratchSpace{} // for gload
- ledger.NewAccount(tx.Sender, 1)
- params := basics.AssetParams{
- Total: 1000,
- Decimals: 2,
- DefaultFrozen: false,
- UnitName: "ALGO",
- AssetName: "",
- URL: string(protocol.PaymentTx),
- Manager: tx.Sender,
- Reserve: tx.Receiver,
- Freeze: tx.Receiver,
- Clawback: tx.Receiver,
- }
- ledger.NewAsset(tx.Sender, 1, params)
- ledger.NewApp(tx.Sender, 1, basics.AppParams{})
- ledger.NewAccount(tx.Receiver, 1000000)
- ledger.NewLocals(tx.Receiver, 1)
- key, err := hex.DecodeString("33343536")
- require.NoError(t, err)
- algoValue := basics.TealValue{Type: basics.TealUintType, Uint: 0x77}
- ledger.NewLocal(tx.Receiver, 1, string(key), algoValue)
- ledger.NewAccount(appAddr(1), 1000000)
// We try to form a snippet that will test every opcode, by sandwiching it
// between arguments that correspond to the opcode's input types, and then
@@ -2477,7 +2442,7 @@ func TestReturnTypes(t *testing.T) {
}
byName := OpsByName[LogicVersion]
- for _, m := range []runMode{modeSig, modeApp} {
+ for _, m := range []RunMode{ModeSig, ModeApp} {
for name, spec := range byName {
// Only try an opcode in its modes
if (m & spec.Modes) == 0 {
@@ -2486,7 +2451,10 @@ func TestReturnTypes(t *testing.T) {
if skipCmd[name] || spec.trusted {
continue
}
+ m, name, spec := m, name, spec
t.Run(fmt.Sprintf("mode=%s,opcode=%s", m, name), func(t *testing.T) {
+ t.Parallel()
+
provideStackInput := true
cmd := name
if special, ok := specialCmd[name]; ok {
@@ -2529,6 +2497,49 @@ func TestReturnTypes(t *testing.T) {
sb.WriteString(cmd + "\n")
ops := testProg(t, sb.String(), AssemblerMaxVersion)
+ ep, tx, ledger := makeSampleEnv()
+
+ tx.Type = protocol.ApplicationCallTx
+ tx.ApplicationID = 1
+ tx.ForeignApps = []basics.AppIndex{tx.ApplicationID}
+ tx.ForeignAssets = []basics.AssetIndex{basics.AssetIndex(1), basics.AssetIndex(1)}
+ tx.Boxes = []transactions.BoxRef{{
+ Name: []byte("3456"),
+ }}
+ ep.TxnGroup[0].Lsig.Args = [][]byte{
+ []byte("aoeu"),
+ []byte("aoeu"),
+ []byte("aoeu2"),
+ []byte("aoeu3"),
+ }
+ // We are going to run with GroupIndex=1, so make tx1 interesting too (so
+ // txn can look at things)
+ ep.TxnGroup[1] = ep.TxnGroup[0]
+
+ ep.pastScratch[0] = &scratchSpace{} // for gload
+ ledger.NewAccount(tx.Sender, 1)
+ params := basics.AssetParams{
+ Total: 1000,
+ Decimals: 2,
+ DefaultFrozen: false,
+ UnitName: "ALGO",
+ AssetName: "",
+ URL: string(protocol.PaymentTx),
+ Manager: tx.Sender,
+ Reserve: tx.Receiver,
+ Freeze: tx.Receiver,
+ Clawback: tx.Receiver,
+ }
+ ledger.NewAsset(tx.Sender, 1, params)
+ ledger.NewApp(tx.Sender, 1, basics.AppParams{})
+ ledger.NewAccount(tx.Receiver, 1000000)
+ ledger.NewLocals(tx.Receiver, 1)
+ key, err := hex.DecodeString("33343536")
+ require.NoError(t, err)
+ algoValue := basics.TealValue{Type: basics.TealUintType, Uint: 0x77}
+ ledger.NewLocal(tx.Receiver, 1, string(key), algoValue)
+ ledger.NewAccount(appAddr(1), 1000000)
+
ep.reset() // for Trace and budget isolation
ep.pastScratch[0] = &scratchSpace{} // for gload
// these allows the box_* opcodes that to work
@@ -2551,7 +2562,7 @@ func TestReturnTypes(t *testing.T) {
// is checked for typing, we can't get hung up on whether it is
// exactly one positive int. But if it fails for any *other*
// reason, we're not doing a good test.
- _, err := eval(ops.Program, &cx)
+ _, err = eval(ops.Program, &cx)
if err != nil {
// Allow the kinds of errors we expect, but fail for stuff
// that indicates the opcode itself failed.
@@ -2710,6 +2721,7 @@ func appAddr(id int) basics.Address {
func TestAppInfo(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
ep, tx, ledger := makeSampleEnv()
require.Equal(t, 888, int(tx.ApplicationID))
diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go
index 5fcbd3f19..6731c1982 100644
--- a/data/transactions/logic/eval_test.go
+++ b/data/transactions/logic/eval_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -229,6 +229,7 @@ func TestMinAvmVersionParamEvalCheckSignature(t *testing.T) {
func TestTxnFieldToTealValue(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
txn := transactions.Transaction{}
groupIndex := 0
@@ -1809,7 +1810,9 @@ func TestTxn(t *testing.T) {
clearOps := testProg(t, "int 1", 1)
for v, source := range tests {
+ v, source := v, source
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
+ t.Parallel()
ops := testProg(t, source, v)
txn := makeSampleTxn()
if v >= appsEnabledVersion {
@@ -2072,7 +2075,9 @@ gtxn 0 Sender
}
for v, source := range tests {
+ v, source := v, source
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
+ t.Parallel()
txn := makeSampleTxn()
// RekeyTo not allowed in v1
if v < rekeyingEnabledVersion {
@@ -2726,7 +2731,9 @@ func TestGload(t *testing.T) {
}
for i, testCase := range cases {
+ i, testCase := i, testCase
t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) {
+ t.Parallel()
sources := testCase.tealSources
// Initialize txgroup
@@ -2746,7 +2753,7 @@ func TestGload(t *testing.T) {
// for more complex group transaction cases
type failureCase struct {
firstTxn transactions.SignedTxn
- runMode runMode
+ runMode RunMode
errContains string
}
@@ -2756,7 +2763,7 @@ func TestGload(t *testing.T) {
Type: protocol.PaymentTx,
},
},
- runMode: modeApp,
+ runMode: ModeApp,
errContains: "can't use gload on non-app call txn with index 0",
}
@@ -2766,13 +2773,15 @@ func TestGload(t *testing.T) {
Type: protocol.ApplicationCallTx,
},
},
- runMode: modeSig,
+ runMode: ModeSig,
errContains: "gload not allowed in current mode",
}
failCases := []failureCase{nonAppCall, logicSigCall}
for j, failCase := range failCases {
+ j, failCase := j, failCase
t.Run(fmt.Sprintf("j=%d", j), func(t *testing.T) {
+ t.Parallel()
appcall := transactions.SignedTxn{
Txn: transactions.Transaction{
@@ -2785,7 +2794,7 @@ func TestGload(t *testing.T) {
program := testProg(t, "gload 0 0", AssemblerMaxVersion).Program
switch failCase.runMode {
- case modeApp:
+ case ModeApp:
testAppBytes(t, program, ep, failCase.errContains)
default:
testLogicBytes(t, program, ep, failCase.errContains, failCase.errContains)
@@ -3106,7 +3115,9 @@ func TestShortBytecblock2(t *testing.T) {
"0026efbfbdefbfbd30",
}
for _, src := range sources {
+ src := src
t.Run(src, func(t *testing.T) {
+ t.Parallel()
program, err := hex.DecodeString(src)
require.NoError(t, err)
testLogicBytes(t, program, defaultEvalParams(), "const bytes list", "const bytes list")
@@ -3125,10 +3136,13 @@ func checkPanic(cx *EvalContext) error {
func TestPanic(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
log := logging.TestingLog(t)
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
+ v := v
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
+ t.Parallel()
ops := testProg(t, `int 1`, v)
var hackedOpcode int
var oldSpec OpSpec
@@ -3300,7 +3314,9 @@ done:
intc_1
`
for _, line := range branches {
+ line := line
t.Run(fmt.Sprintf("branch=%s", line), func(t *testing.T) {
+ t.Parallel()
source := fmt.Sprintf(template, line)
ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion)
require.NoError(t, err)
@@ -3712,27 +3728,37 @@ main:
}
func BenchmarkByteLogic(b *testing.B) {
+ e64 := "byte 0x8090a0b0c0d0e0f0;"
+ o64 := "byte 0x1020304050607080;"
+ hex128e := "90a0b0c0d0e0f0001020304050607080"
+ hex128o := "102030405060708090a0b0c0d0e0f000"
+ e128 := "byte 0x" + strings.Repeat(hex128e, 1) + ";"
+ o128 := "byte 0x" + strings.Repeat(hex128o, 1) + ";"
+ e256 := "byte 0x" + strings.Repeat(hex128e, 2) + ";"
+ o256 := "byte 0x" + strings.Repeat(hex128o, 2) + ";"
+ e512 := "byte 0x" + strings.Repeat(hex128e, 4) + ";"
+ o512 := "byte 0x" + strings.Repeat(hex128o, 4) + ";"
+
benches := [][]string{
- {"b&", "", "byte 0x012345678901feab; byte 0x01ffffffffffffff; b&; pop", "int 1"},
- {"b|", "", "byte 0x0ffff1234576abef; byte 0x1202120212021202; b|; pop", "int 1"},
- {"b^", "", "byte 0x0ffff1234576abef; byte 0x1202120212021202; b^; pop", "int 1"},
- {"b~", "byte 0x0123457673624736", "b~", "pop; int 1"},
-
- {"b&big",
- "byte 0x0123457601234576012345760123457601234576012345760123457601234576",
- "byte 0x01ffffffffffffff01ffffffffffffff01234576012345760123457601234576; b&",
- "pop; int 1"},
- {"b|big",
- "byte 0x0123457601234576012345760123457601234576012345760123457601234576",
- "byte 0xffffff01ffffffffffffff01234576012345760123457601234576; b|",
- "pop; int 1"},
- {"b^big", "", // u256^u256
- `byte 0x123457601234576012345760123457601234576012345760123457601234576a
- byte 0xf123457601234576012345760123457601234576012345760123457601234576; b^; pop`,
- "int 1"},
- {"b~big", "byte 0xa123457601234576012345760123457601234576012345760123457601234576",
- "b~",
- "pop; int 1"},
+ {"b& 8", "", e64 + o64 + "b&; pop", "int 1"},
+ {"b| 8", "", e64 + o64 + "b|; pop", "int 1"},
+ {"b^ 8", "", e64 + o64 + "b^; pop", "int 1"},
+ {"b~ 8", e64, "b~", "pop; int 1"},
+
+ {"b& 16", "", e128 + o128 + "b&; pop", "int 1"},
+ {"b| 16", "", e128 + o128 + "b|; pop", "int 1"},
+ {"b^ 16", "", e128 + o128 + "b^; pop", "int 1"},
+ {"b~ 16", e128, "b~", "pop; int 1"},
+
+ {"b& 32", "", e256 + o256 + "b&; pop", "int 1"},
+ {"b| 32", "", e256 + o256 + "b|; pop", "int 1"},
+ {"b^ 32", "", e256 + o256 + "b^; pop", "int 1"},
+ {"b~ 32", e256, "b~", "pop; int 1"},
+
+ {"b& 64", "", e512 + o512 + "b&; pop", "int 1"},
+ {"b| 64", "", e512 + o512 + "b|; pop", "int 1"},
+ {"b^ 64", "", e512 + o512 + "b^; pop", "int 1"},
+ {"b~ 64", e512, "b~", "pop; int 1"},
}
for _, bench := range benches {
b.Run(bench[0], func(b *testing.B) {
@@ -3743,44 +3769,79 @@ func BenchmarkByteLogic(b *testing.B) {
}
func BenchmarkByteMath(b *testing.B) {
+ u64 := "byte 0x8090a0b0c0d0e0f0;"
+ hex128 := "102030405060708090a0b0c0d0e0f000"
+ u128 := "byte 0x" + strings.Repeat(hex128, 1) + ";"
+ u256 := "byte 0x" + strings.Repeat(hex128, 2) + ";"
+ u512 := "byte 0x" + strings.Repeat(hex128, 4) + ";"
+
benches := [][]string{
- {"bpop", "", "byte 0x01ffffffffffffff; pop", "int 1"},
-
- {"b+", "byte 0x01234576", "byte 0x01ffffffffffffff; b+", "pop; int 1"},
- {"b-", "byte 0x0ffff1234576", "byte 0x1202; b-", "pop; int 1"},
- {"b*", "", "byte 0x01234576; byte 0x0223627389; b*; pop", "int 1"},
- {"b/", "", "byte 0x0123457673624736; byte 0x0223627389; b/; pop", "int 1"},
- {"b%", "", "byte 0x0123457673624736; byte 0x0223627389; b/; pop", "int 1"},
- {"bsqrt", "", "byte 0x0123457673624736; bsqrt; pop", "int 1"},
-
- {"b+big", // u256 + u256
- "byte 0x0123457601234576012345760123457601234576012345760123457601234576",
- "byte 0x01ffffffffffffff01ffffffffffffff01234576012345760123457601234576; b+",
- "pop; int 1"},
- {"b-big", // second is a bit small, so we can subtract it over and over
- "byte 0x0123457601234576012345760123457601234576012345760123457601234576",
- "byte 0xffffff01ffffffffffffff01234576012345760123457601234576; b-",
- "pop; int 1"},
- {"b*big", "", // u256*u256
- `byte 0xa123457601234576012345760123457601234576012345760123457601234576
- byte 0xf123457601234576012345760123457601234576012345760123457601234576; b*; pop`,
- "int 1"},
- {"b/big", "", // u256 / u128 (half sized divisor seems pessimal)
- `byte 0xa123457601234576012345760123457601234576012345760123457601234576
- byte 0x34576012345760123457601234576312; b/; pop`,
- "int 1"},
- {"b%big", "", // u256 / u128 (half sized divisor seems pessimal)
- `byte 0xa123457601234576012345760123457601234576012345760123457601234576
- byte 0x34576012345760123457601234576312; b/; pop`,
- "int 1"},
- {"bsqrt-big", "",
- `byte 0xa123457601234576012345760123457601234576012345760123457601234576
- bsqrt; pop`,
- "int 1"},
+ {"bytec", u128 + "pop"},
+
+ {"b+ 128", u128 + u128 + "b+; pop"},
+ {"b- 128", u128 + u128 + "b-; pop"},
+ {"b* 128", u128 + u128 + "b*; pop"},
+ // half sized divisor seems pessimal for / and %
+ {"b/ 128", u128 + u64 + "b/; pop"},
+ {"b% 128", u128 + u64 + "b%; pop"},
+ {"bsqrt 128", u128 + "bsqrt; pop"},
+
+ {"b+ 256", u256 + u256 + "b+; pop"},
+ {"b- 256", u256 + u256 + "b-; pop"},
+ {"b* 256", u256 + u256 + "b*; pop"},
+ {"b/ 256", u256 + u128 + "b/; pop"},
+ {"b% 256", u256 + u128 + "b%; pop"},
+ {"bsqrt 256", u256 + "bsqrt; pop"},
+
+ {"b+ 512", u512 + u512 + "b+; pop"},
+ {"b- 512", u512 + u512 + "b-; pop"},
+ {"b* 512", u512 + u512 + "b*; pop"},
+ {"b/ 512", u512 + u256 + "b/; pop"},
+ {"b% 512", u512 + u256 + "b%; pop"},
+ {"bsqrt 512", u512 + "bsqrt; pop"},
+
+ {"bytec recheck", u128 + "pop"},
}
for _, bench := range benches {
b.Run(bench[0], func(b *testing.B) {
- benchmarkOperation(b, bench[1], bench[2], bench[3])
+ b.ReportAllocs()
+ benchmarkOperation(b, "", bench[1], "int 1")
+ })
+ }
+}
+
+func BenchmarkByteCompare(b *testing.B) {
+ u64 := "byte 0x8090a0b0c0d0e0f0;"
+ hex128 := "102030405060708090a0b0c0d0e0f000"
+ u128 := "byte 0x" + strings.Repeat(hex128, 1) + ";"
+ u256 := "byte 0x" + strings.Repeat(hex128, 2) + ";"
+ u512 := "byte 0x" + strings.Repeat(hex128, 4) + ";"
+ //u4k := "byte 0x" + strings.Repeat(hex128, 256) + ";"
+
+ benches := [][]string{
+ {"b== 64", u64 + u64 + "b==; pop"},
+ {"b< 64", u64 + u64 + "b<; pop"},
+ {"b<= 64", u64 + u64 + "b<=; pop"},
+ {"b== 128", u128 + u128 + "b==; pop"},
+ {"b< 128", u128 + u128 + "b<; pop"},
+ {"b<= 128", u128 + u128 + "b<=; pop"},
+ {"b== 256", u256 + u256 + "b==; pop"},
+ {"b< 256", u256 + u256 + "b<; pop"},
+ {"b<= 256", u256 + u256 + "b<=; pop"},
+ {"b== 512", u512 + u512 + "b==; pop"},
+ {"b< 512", u512 + u512 + "b<; pop"},
+ {"b<= 512", u512 + u512 + "b<=; pop"},
+ // These can only be run with the maxByteMathSize check removed. They
+ // show that we can remove that check in a later AVM version, as there
+ // is no appreciable cost to even a 4k compare.
+ // {"b== 4k", u4k + u4k + "b==; pop"},
+ // {"b< 4k", u4k + u4k + "b<; pop"},
+ // {"b<= 4k", u4k + u4k + "b<=; pop"},
+ }
+ for _, bench := range benches {
+ b.Run(bench[0], func(b *testing.B) {
+ b.ReportAllocs()
+ benchmarkOperation(b, "", bench[1], "int 1")
})
}
}
@@ -4135,7 +4196,9 @@ func TestAnyRekeyToOrApplicationRaisesMinAvmVersion(t *testing.T) {
}
for ci, cse := range cases {
+ ci, cse := ci, cse
t.Run(fmt.Sprintf("ci=%d", ci), func(t *testing.T) {
+ t.Parallel()
ep := defaultEvalParams(cse.group...)
// Computed MinAvmVersion should be == validFromVersion
@@ -4853,9 +4916,23 @@ func TestBytesCompare(t *testing.T) {
testPanics(t, "byte 0x10; int 65; bzero; b<=", 4)
testAccepts(t, "byte 0x10; int 64; bzero; b>", 4)
testPanics(t, "byte 0x10; int 65; bzero; b>", 4)
+ testAccepts(t, "byte 0x1010; byte 0x10; b<; !", 4)
+
+ testAccepts(t, "byte 0x2000; byte 0x70; b<; !", 4)
+ testAccepts(t, "byte 0x7000; byte 0x20; b<; !", 4)
+
+ // All zero input are interesting, because they lead to bytes.Compare being
+ // called with nils. Show that is correct.
+ testAccepts(t, "byte 0x10; byte 0x00; b<; !", 4)
+ testAccepts(t, "byte 0x10; byte 0x0000; b<; !", 4)
+ testAccepts(t, "byte 0x00; byte 0x10; b<", 4)
+ testAccepts(t, "byte 0x0000; byte 0x10; b<", 4)
+ testAccepts(t, "byte 0x0000; byte 0x00; b<; !", 4)
+ testAccepts(t, "byte 0x; byte 0x00; b==", 4)
testAccepts(t, "byte 0x11; byte 0x10; b>", 4)
testAccepts(t, "byte 0x11; byte 0x0010; b>", 4)
+ testAccepts(t, "byte 0x1010; byte 0x11; b>", 4)
testAccepts(t, "byte 0x11; byte 0x10; b>=", 4)
testAccepts(t, "byte 0x11; byte 0x0011; b>=", 4)
@@ -4964,7 +5041,7 @@ func TestLog(t *testing.T) {
msg := strings.Repeat("a", 400)
failCases := []struct {
source string
- runMode runMode
+ runMode RunMode
errContains string
// For cases where assembly errors, we manually put in the bytes
assembledBytes []byte
@@ -4972,44 +5049,44 @@ func TestLog(t *testing.T) {
{
source: fmt.Sprintf(`byte "%s"; log; int 1`, strings.Repeat("a", maxLogSize+1)),
errContains: fmt.Sprintf("> %d bytes limit", maxLogSize),
- runMode: modeApp,
+ runMode: ModeApp,
},
{
source: fmt.Sprintf(`byte "%s"; log; byte "%s"; log; byte "%s"; log; int 1`, msg, msg, msg),
errContains: fmt.Sprintf("> %d bytes limit", maxLogSize),
- runMode: modeApp,
+ runMode: ModeApp,
},
{
source: fmt.Sprintf(`%s; int 1`, strings.Repeat(`byte "a"; log; `, maxLogCalls+1)),
errContains: "too many log calls",
- runMode: modeApp,
+ runMode: ModeApp,
},
{
source: `int 1; loop: byte "a"; log; int 1; +; dup; int 35; <; bnz loop;`,
errContains: "too many log calls",
- runMode: modeApp,
+ runMode: ModeApp,
},
{
source: fmt.Sprintf(`int 1; loop: byte "%s"; log; int 1; +; dup; int 6; <; bnz loop;`, strings.Repeat(`a`, 400)),
errContains: fmt.Sprintf("> %d bytes limit", maxLogSize),
- runMode: modeApp,
+ runMode: ModeApp,
},
{
source: `load 0; log`,
errContains: "log arg 0 wanted []byte but got uint64",
- runMode: modeApp,
+ runMode: ModeApp,
assembledBytes: []byte{byte(ep.Proto.LogicSigVersion), 0x34, 0x00, 0xb0},
},
{
source: `byte "a logging message"; log; int 1`,
errContains: "log not allowed in current mode",
- runMode: modeSig,
+ runMode: ModeSig,
},
}
for _, c := range failCases {
switch c.runMode {
- case modeApp:
+ case ModeApp:
if c.assembledBytes == nil {
testApp(t, c.source, ep, c.errContains)
} else {
@@ -5036,7 +5113,9 @@ func TestPcDetails(t *testing.T) {
{"b end; end:", 4, ""},
}
for i, test := range tests {
+ i, test := i, test
t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) {
+ t.Parallel()
ops := testProg(t, test.source, LogicVersion)
ep, _, _ := makeSampleEnv()
ep.Trace = &strings.Builder{}
diff --git a/data/transactions/logic/export_test.go b/data/transactions/logic/export_test.go
index 67346482a..de151bfd9 100644
--- a/data/transactions/logic/export_test.go
+++ b/data/transactions/logic/export_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go
index 0b62a5239..0af2079a3 100644
--- a/data/transactions/logic/fields.go
+++ b/data/transactions/logic/fields.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -537,7 +537,7 @@ var GlobalFieldNames [invalidGlobalField]string
type globalFieldSpec struct {
field GlobalField
ftype StackType
- mode runMode
+ mode RunMode
version uint64
doc string
}
@@ -556,7 +556,7 @@ func (fs globalFieldSpec) Version() uint64 {
}
func (fs globalFieldSpec) Note() string {
note := fs.doc
- if fs.mode == modeApp {
+ if fs.mode == ModeApp {
note = addExtra(note, "Application mode only.")
}
// There are no Signature mode only globals
@@ -572,21 +572,21 @@ var globalFieldSpecs = [...]globalFieldSpec{
{GroupSize, StackUint64, modeAny, 0,
"Number of transactions in this atomic transaction group. At least 1"},
{LogicSigVersion, StackUint64, modeAny, 2, "Maximum supported version"},
- {Round, StackUint64, modeApp, 2, "Current round number"},
- {LatestTimestamp, StackUint64, modeApp, 2,
+ {Round, StackUint64, ModeApp, 2, "Current round number"},
+ {LatestTimestamp, StackUint64, ModeApp, 2,
"Last confirmed block UNIX timestamp. Fails if negative"},
- {CurrentApplicationID, StackUint64, modeApp, 2, "ID of current application executing"},
- {CreatorAddress, StackBytes, modeApp, 3,
+ {CurrentApplicationID, StackUint64, ModeApp, 2, "ID of current application executing"},
+ {CreatorAddress, StackBytes, ModeApp, 3,
"Address of the creator of the current application"},
- {CurrentApplicationAddress, StackBytes, modeApp, 5,
+ {CurrentApplicationAddress, StackBytes, ModeApp, 5,
"Address that the current application controls"},
{GroupID, StackBytes, modeAny, 5,
"ID of the transaction group. 32 zero bytes if the transaction is not part of a group."},
{OpcodeBudget, StackUint64, modeAny, 6,
"The remaining cost that can be spent by opcodes in this program."},
- {CallerApplicationID, StackUint64, modeApp, 6,
+ {CallerApplicationID, StackUint64, ModeApp, 6,
"The application ID of the application that called this application. 0 if this application is at the top-level."},
- {CallerApplicationAddress, StackBytes, modeApp, 6,
+ {CallerApplicationAddress, StackBytes, ModeApp, 6,
"The application address of the application that called this application. ZeroAddress if this application is at the top-level."},
}
diff --git a/data/transactions/logic/fields_test.go b/data/transactions/logic/fields_test.go
index 5ae042294..cbe425d5f 100644
--- a/data/transactions/logic/fields_test.go
+++ b/data/transactions/logic/fields_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/frames.go b/data/transactions/logic/frames.go
index 1acc0c3c2..4eda0e933 100644
--- a/data/transactions/logic/frames.go
+++ b/data/transactions/logic/frames.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/frames_test.go b/data/transactions/logic/frames_test.go
index b02714a88..c46a8dd84 100644
--- a/data/transactions/logic/frames_test.go
+++ b/data/transactions/logic/frames_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/jsonspec_test.go b/data/transactions/logic/jsonspec_test.go
index 3ebe131e8..a2bfcfff2 100644
--- a/data/transactions/logic/jsonspec_test.go
+++ b/data/transactions/logic/jsonspec_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -32,6 +32,7 @@ import (
func TestParseScalar(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
intScalar := `{"key0": 4160}`
_, err := parseJSON([]byte(intScalar))
require.NoError(t, err)
@@ -42,6 +43,7 @@ func TestParseScalar(t *testing.T) {
func TestParseTrailingCommas(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
for i := 1; i <= 10; i++ {
commas := strings.Repeat(",", i)
intScalar := `{"key0": 4160` + commas + `}`
@@ -55,6 +57,7 @@ func TestParseTrailingCommas(t *testing.T) {
func TestParseComments(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
text := `{"key0": /*comment*/"algo"}`
_, err := parseJSON([]byte(text))
require.Error(t, err)
@@ -65,6 +68,7 @@ func TestParseComments(t *testing.T) {
func TestParseUnclosed(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
text := `{"key0": ["algo"}`
_, err := parseJSON([]byte(text))
require.Error(t, err)
@@ -84,6 +88,7 @@ func TestParseUnclosed(t *testing.T) {
func TestParseNested(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
text := `{"key0": [[1,2,3],[4,5,6]], "key1":{"key10":{"key100":"algo"}}}`
_, err := parseJSON([]byte(text))
require.NoError(t, err)
@@ -91,6 +96,7 @@ func TestParseNested(t *testing.T) {
func TestParseWhiteSpace(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
//empty text
text := ""
_, err := parseJSON([]byte(text))
@@ -107,6 +113,7 @@ func TestParseWhiteSpace(t *testing.T) {
func TestParseSpecialValues(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
text := `{"key0": NaN}`
_, err := parseJSON([]byte(text))
require.Error(t, err)
@@ -129,6 +136,7 @@ func TestParseSpecialValues(t *testing.T) {
func TestParseHexValue(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
text := `{"key0": 0x1}`
_, err := parseJSON([]byte(text))
require.Error(t, err)
@@ -139,6 +147,7 @@ func TestParseHexValue(t *testing.T) {
func TestParseBigNum(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
// values in range uint64 parsed correctly
// parse 0
text := `{"key0":0}`
@@ -162,6 +171,7 @@ func TestParseBigNum(t *testing.T) {
func TestParseArrays(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
text := `{"key0": [,1,]}`
_, err := parseJSON([]byte(text))
require.Error(t, err)
@@ -175,6 +185,7 @@ func TestParseArrays(t *testing.T) {
func TestParseKeys(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
text := `{"": 1}`
_, err := parseJSON([]byte(text))
require.NoError(t, err)
@@ -211,6 +222,7 @@ func TestParseKeys(t *testing.T) {
func TestParseFileEncoding(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
// create utf-8, utf-16, and utf-32 encoded text and check which is supported by json
// it appears that json only supports utf-8 encoded json text
@@ -250,6 +262,7 @@ func TestParseFileEncoding(t *testing.T) {
func TestParseByteOrderMark(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
// byte order mark is not allowed at the beginning of a JSON text,
// it is treated as an error
text := "\uFEFF{\"key0\": 1}"
@@ -259,6 +272,7 @@ func TestParseByteOrderMark(t *testing.T) {
func TestParseControlChar(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
// control chars (u0000 - u001F) must be escaped
for i := 0x0; i <= 0x1f; i++ {
text := fmt.Sprintf("{\"key0\":\"\\u%04X\"}", i)
@@ -269,6 +283,7 @@ func TestParseControlChar(t *testing.T) {
func TestParseEscapeChar(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
// escaped control char
text := "{\"key0\": \"\\u0000\"}"
_, err := parseJSON([]byte(text))
@@ -287,6 +302,7 @@ func TestParseEscapeChar(t *testing.T) {
func TestParseEscapedInvalidChar(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
// unicode escape sequence remains in string
// accepted surrogate pair
text := `{"key0": "\uD801\udc37"}`
@@ -307,6 +323,7 @@ func TestParseEscapedInvalidChar(t *testing.T) {
func TestParseRawNonUnicodeChar(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
text := `{"key0": "πζθ"}`
_, err := parseJSON([]byte(text))
require.NoError(t, err)
diff --git a/data/transactions/logic/langspec.json b/data/transactions/logic/langspec.json
index 148f83e07..cbfb4b0d9 100644
--- a/data/transactions/logic/langspec.json
+++ b/data/transactions/logic/langspec.json
@@ -67,6 +67,10 @@
"Args": "BBBBB",
"Returns": "U",
"Size": 2,
+ "ArgEnum": [
+ "Secp256k1",
+ "Secp256r1"
+ ],
"Doc": "for (data A, signature B, C and pubkey D, E) verify the signature of the data against the pubkey =\u003e {0 or 1}",
"DocExtra": "The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.",
"ImmediateNote": "{uint8 curve index}",
@@ -81,6 +85,10 @@
"Args": "B",
"Returns": "BB",
"Size": 2,
+ "ArgEnum": [
+ "Secp256k1",
+ "Secp256r1"
+ ],
"Doc": "decompress pubkey A into components X, Y",
"DocExtra": "The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.",
"ImmediateNote": "{uint8 curve index}",
@@ -95,6 +103,10 @@
"Args": "BUBB",
"Returns": "BB",
"Size": 2,
+ "ArgEnum": [
+ "Secp256k1",
+ "Secp256r1"
+ ],
"Doc": "for (data A, recovery id B, signature C, D) recover a public key",
"DocExtra": "S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.",
"ImmediateNote": "{uint8 curve index}",
@@ -470,7 +482,7 @@
"Size": 0,
"Doc": "prepare block of byte-array constants for use by bytec",
"DocExtra": "`bytecblock` loads the following program bytes into an array of byte-array constants in the evaluator. These constants can be referred to by `bytec` and `bytec_*` which will push the value onto the stack. Subsequent calls to `bytecblock` reset and replace the bytes constants available to the script.",
- "ImmediateNote": "{varuint count} [({varuint value length} bytes), ...]",
+ "ImmediateNote": "{varuint count} [({varuint length} bytes), ...]",
"IntroducedVersion": 1,
"Groups": [
"Loading Values"
@@ -538,7 +550,7 @@
"Returns": "B",
"Size": 2,
"Doc": "Nth LogicSig argument",
- "ImmediateNote": "{uint8 arg index N}",
+ "ImmediateNote": "{uint8 arg index}",
"IntroducedVersion": 1,
"Groups": [
"Loading Values"
@@ -1410,6 +1422,11 @@
"Args": "B",
"Returns": "B",
"Size": 2,
+ "ArgEnum": [
+ "URLEncoding",
+ "StdEncoding"
+ ],
+ "ArgEnumTypes": "..",
"Doc": "decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E",
"DocExtra": "*Warning*: Usage should be restricted to very rare use cases. In almost all cases, smart contracts should directly handle non-encoded byte-strings.\tThis opcode should only be used in cases where base64 is the only available option, e.g. interoperability with a third-party that only signs base64 strings.\n\n Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See [RFC 4648 sections 4 and 5](https://rfc-editor.org/rfc/rfc4648.html#section-4). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.",
"ImmediateNote": "{uint8 encoding index}",
@@ -1424,9 +1441,15 @@
"Args": "BB",
"Returns": ".",
"Size": 2,
+ "ArgEnum": [
+ "JSONString",
+ "JSONUint64",
+ "JSONObject"
+ ],
+ "ArgEnumTypes": "BUB",
"Doc": "key B's value, of type R, from a [valid](jsonspec.md) utf-8 encoded json object A",
"DocExtra": "*Warning*: Usage should be restricted to very rare use cases, as JSON decoding is expensive and quite limited. In addition, JSON objects are large and not optimized for size.\n\nAlmost all smart contracts should use simpler and smaller methods (such as the [ABI](https://arc.algorand.foundation/ARCs/arc-0004). This opcode should only be used in cases where JSON is only available option, e.g. when a third-party only signs JSON.",
- "ImmediateNote": "{uint8 return type}",
+ "ImmediateNote": "{uint8 return type index}",
"IntroducedVersion": 7,
"Groups": [
"Byte Array Manipulation"
@@ -1704,7 +1727,7 @@
"Size": 0,
"Doc": "push sequences of immediate byte arrays to stack (first byte array being deepest)",
"DocExtra": "pushbytess args are not added to the bytecblock during assembly processes",
- "ImmediateNote": "{varuint count} [({varuint value length} bytes), ...]",
+ "ImmediateNote": "{varuint count} [({varuint length} bytes), ...]",
"IntroducedVersion": 8,
"Groups": [
"Loading Values"
@@ -2651,6 +2674,9 @@
"Args": "BBB",
"Returns": "BU",
"Size": 2,
+ "ArgEnum": [
+ "VrfAlgorand"
+ ],
"Doc": "Verify the proof B of message A against pubkey C. Returns vrf output and verification flag.",
"DocExtra": "`VrfAlgorand` is the VRF used in Algorand. It is ECVRF-ED25519-SHA512-Elligator2, specified in the IETF internet draft [draft-irtf-cfrg-vrf-03](https://datatracker.ietf.org/doc/draft-irtf-cfrg-vrf/03/).",
"ImmediateNote": "{uint8 parameters index}",
@@ -2665,8 +2691,13 @@
"Args": "U",
"Returns": ".",
"Size": 2,
+ "ArgEnum": [
+ "BlkSeed",
+ "BlkTimestamp"
+ ],
+ "ArgEnumTypes": "BU",
"Doc": "field F of block A. Fail unless A falls between txn.LastValid-1002 and txn.FirstValid (exclusive)",
- "ImmediateNote": "{uint8 block field}",
+ "ImmediateNote": "{uint8 block field index}",
"IntroducedVersion": 7,
"Groups": [
"State Access"
diff --git a/data/transactions/logic/ledger_test.go b/data/transactions/logic/ledger_test.go
index 570967305..f56f349f1 100644
--- a/data/transactions/logic/ledger_test.go
+++ b/data/transactions/logic/ledger_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/mocktracer/tracer.go b/data/transactions/logic/mocktracer/tracer.go
new file mode 100644
index 000000000..967798ec9
--- /dev/null
+++ b/data/transactions/logic/mocktracer/tracer.go
@@ -0,0 +1,147 @@
+// 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 mocktracer
+
+import (
+ "github.com/algorand/go-algorand/data/transactions"
+ "github.com/algorand/go-algorand/data/transactions/logic"
+ "github.com/algorand/go-algorand/protocol"
+)
+
+// EventType represents a type of logic.EvalTracer event
+type EventType string
+
+const (
+ // BeforeTxnGroupEvent represents the logic.EvalTracer.BeforeTxnGroup event
+ BeforeTxnGroupEvent EventType = "BeforeTxnGroup"
+ // AfterTxnGroupEvent represents the logic.EvalTracer.AfterTxnGroup event
+ AfterTxnGroupEvent EventType = "AfterTxnGroup"
+ // BeforeTxnEvent represents the logic.EvalTracer.BeforeTxn event
+ BeforeTxnEvent EventType = "BeforeTxn"
+ // AfterTxnEvent represents the logic.EvalTracer.AfterTxn event
+ AfterTxnEvent EventType = "AfterTxn"
+ // BeforeProgramEvent represents the logic.EvalTracer.BeforeProgram event
+ BeforeProgramEvent EventType = "BeforeProgram"
+ // AfterProgramEvent represents the logic.EvalTracer.AfterProgram event
+ AfterProgramEvent EventType = "AfterProgram"
+ // BeforeOpcodeEvent represents the logic.EvalTracer.BeforeOpcode event
+ BeforeOpcodeEvent EventType = "BeforeOpcode"
+ // AfterOpcodeEvent represents the logic.EvalTracer.AfterOpcode event
+ AfterOpcodeEvent EventType = "AfterOpcode"
+)
+
+// Event represents a logic.EvalTracer event
+type Event struct {
+ Type EventType
+
+ // only for BeforeProgram and AfterProgram
+ LogicEvalMode logic.RunMode
+
+ // only for BeforeTxn and AfterTxn
+ TxnType protocol.TxType
+
+ // only for AfterTxn
+ TxnApplyData transactions.ApplyData
+
+ // only for BeforeTxnGroup and AfterTxnGroup
+ GroupSize int
+}
+
+// BeforeTxnGroup creates a new Event with the type BeforeTxnGroupEvent
+func BeforeTxnGroup(groupSize int) Event {
+ return Event{Type: BeforeTxnGroupEvent, GroupSize: groupSize}
+}
+
+// AfterTxnGroup creates a new Event with the type AfterTxnGroupEvent
+func AfterTxnGroup(groupSize int) Event {
+ return Event{Type: AfterTxnGroupEvent, GroupSize: groupSize}
+}
+
+// BeforeProgram creates a new Event with the type BeforeProgramEvent
+func BeforeProgram(mode logic.RunMode) Event {
+ return Event{Type: BeforeProgramEvent, LogicEvalMode: mode}
+}
+
+// BeforeTxn creates a new Event with the type BeforeTxnEvent
+func BeforeTxn(txnType protocol.TxType) Event {
+ return Event{Type: BeforeTxnEvent, TxnType: txnType}
+}
+
+// AfterTxn creates a new Event with the type AfterTxnEvent
+func AfterTxn(txnType protocol.TxType, ad transactions.ApplyData) Event {
+ return Event{Type: AfterTxnEvent, TxnType: txnType, TxnApplyData: ad}
+}
+
+// AfterProgram creates a new Event with the type AfterProgramEvent
+func AfterProgram(mode logic.RunMode) Event {
+ return Event{Type: AfterProgramEvent, LogicEvalMode: mode}
+}
+
+// BeforeOpcode creates a new Event with the type BeforeOpcodeEvent
+func BeforeOpcode() Event {
+ return Event{Type: BeforeOpcodeEvent}
+}
+
+// AfterOpcode creates a new Event with the type AfterOpcodeEvent
+func AfterOpcode() Event {
+ return Event{Type: AfterOpcodeEvent}
+}
+
+// Tracer is a mock tracer that implements logic.EvalTracer
+type Tracer struct {
+ Events []Event
+}
+
+// BeforeTxnGroup mocks the logic.EvalTracer.BeforeTxnGroup method
+func (d *Tracer) BeforeTxnGroup(ep *logic.EvalParams) {
+ d.Events = append(d.Events, BeforeTxnGroup(len(ep.TxnGroup)))
+}
+
+// AfterTxnGroup mocks the logic.EvalTracer.AfterTxnGroup method
+func (d *Tracer) AfterTxnGroup(ep *logic.EvalParams) {
+ d.Events = append(d.Events, AfterTxnGroup(len(ep.TxnGroup)))
+}
+
+// BeforeTxn mocks the logic.EvalTracer.BeforeTxn method
+func (d *Tracer) BeforeTxn(ep *logic.EvalParams, groupIndex int) {
+ d.Events = append(d.Events, BeforeTxn(ep.TxnGroup[groupIndex].Txn.Type))
+}
+
+// AfterTxn mocks the logic.EvalTracer.AfterTxn method
+func (d *Tracer) AfterTxn(ep *logic.EvalParams, groupIndex int, ad transactions.ApplyData) {
+ d.Events = append(d.Events, AfterTxn(ep.TxnGroup[groupIndex].Txn.Type, ad))
+}
+
+// BeforeProgram mocks the logic.EvalTracer.BeforeProgram method
+func (d *Tracer) BeforeProgram(cx *logic.EvalContext) {
+ d.Events = append(d.Events, BeforeProgram(cx.RunMode()))
+}
+
+// AfterProgram mocks the logic.EvalTracer.AfterProgram method
+func (d *Tracer) AfterProgram(cx *logic.EvalContext, evalError error) {
+ d.Events = append(d.Events, AfterProgram(cx.RunMode()))
+}
+
+// BeforeOpcode mocks the logic.EvalTracer.BeforeOpcode method
+func (d *Tracer) BeforeOpcode(cx *logic.EvalContext) {
+ d.Events = append(d.Events, BeforeOpcode())
+}
+
+// AfterOpcode mocks the logic.EvalTracer.AfterOpcode method
+func (d *Tracer) AfterOpcode(cx *logic.EvalContext, evalError error) {
+ d.Events = append(d.Events, AfterOpcode())
+}
diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go
index 38dde2e08..8f47b57ca 100644
--- a/data/transactions/logic/opcodes.go
+++ b/data/transactions/logic/opcodes.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -121,7 +121,7 @@ type OpDetails struct {
check checkFunc // static check bytecode (and determine size)
refine refineFunc // refine arg/return types based on ProgramKnowledge at assembly time
- Modes runMode // all modes that opcode can run in. i.e (cx.mode & Modes) != 0 allows
+ Modes RunMode // all modes that opcode can run in. i.e (cx.mode & Modes) != 0 allows
FullCost linearCost // if non-zero, the cost of the opcode, no immediates matter
Size int // if non-zero, the known size of opcode. if 0, check() determines.
@@ -221,13 +221,13 @@ func (d OpDetails) costs(cost int) OpDetails {
return d
}
-func only(m runMode) OpDetails {
+func only(m RunMode) OpDetails {
d := detDefault()
d.Modes = m
return d
}
-func (d OpDetails) only(m runMode) OpDetails {
+func (d OpDetails) only(m RunMode) OpDetails {
d.Modes = m
return d
}
@@ -419,7 +419,7 @@ var OpSpecs = []OpSpec{
{0x03, "sha512_256", opSHA512_256, proto("b:b"), 7, unlimitedStorage, costByLength(17, 5, 8)},
*/
- {0x04, "ed25519verify", opEd25519Verify, proto("bbb:i"), 1, costly(1900).only(modeSig)},
+ {0x04, "ed25519verify", opEd25519Verify, proto("bbb:i"), 1, costly(1900).only(ModeSig)},
{0x04, "ed25519verify", opEd25519Verify, proto("bbb:i"), 5, costly(1900)},
{0x05, "ecdsa_verify", opEcdsaVerify, proto("bbbbb:i"), 5, costByField("v", &EcdsaCurves, ecdsaVerifyCosts)},
@@ -463,11 +463,11 @@ var OpSpecs = []OpSpec{
{0x29, "bytec_1", opByteConst1, proto(":b"), 1, detDefault()},
{0x2a, "bytec_2", opByteConst2, proto(":b"), 1, detDefault()},
{0x2b, "bytec_3", opByteConst3, proto(":b"), 1, detDefault()},
- {0x2c, "arg", opArg, proto(":b"), 1, immediates("n").only(modeSig).assembler(asmArg)},
- {0x2d, "arg_0", opArg0, proto(":b"), 1, only(modeSig)},
- {0x2e, "arg_1", opArg1, proto(":b"), 1, only(modeSig)},
- {0x2f, "arg_2", opArg2, proto(":b"), 1, only(modeSig)},
- {0x30, "arg_3", opArg3, proto(":b"), 1, only(modeSig)},
+ {0x2c, "arg", opArg, proto(":b"), 1, immediates("n").only(ModeSig).assembler(asmArg)},
+ {0x2d, "arg_0", opArg0, proto(":b"), 1, only(ModeSig)},
+ {0x2e, "arg_1", opArg1, proto(":b"), 1, only(ModeSig)},
+ {0x2f, "arg_2", opArg2, proto(":b"), 1, only(ModeSig)},
+ {0x30, "arg_3", opArg3, proto(":b"), 1, only(ModeSig)},
// txn, gtxn, and gtxns are also implemented as pseudoOps to choose
// between scalar and array version based on number of immediates.
{0x31, "txn", opTxn, proto(":a"), 1, field("f", &TxnScalarFields)},
@@ -481,11 +481,11 @@ var OpSpecs = []OpSpec{
{0x38, "gtxns", opGtxns, proto("i:a"), 3, immediates("f").field("f", &TxnScalarFields)},
{0x39, "gtxnsa", opGtxnsa, proto("i:a"), 3, immediates("f", "i").field("f", &TxnArrayFields)},
// Group scratch space access
- {0x3a, "gload", opGload, proto(":a"), 4, immediates("t", "i").only(modeApp)},
- {0x3b, "gloads", opGloads, proto("i:a"), 4, immediates("i").only(modeApp)},
+ {0x3a, "gload", opGload, proto(":a"), 4, immediates("t", "i").only(ModeApp)},
+ {0x3b, "gloads", opGloads, proto("i:a"), 4, immediates("i").only(ModeApp)},
// Access creatable IDs (consider deprecating, as txn CreatedAssetID, CreatedApplicationID should be enough
- {0x3c, "gaid", opGaid, proto(":i"), 4, immediates("t").only(modeApp)},
- {0x3d, "gaids", opGaids, proto("i:i"), 4, only(modeApp)},
+ {0x3c, "gaid", opGaid, proto(":i"), 4, immediates("t").only(ModeApp)},
+ {0x3d, "gaids", opGaids, proto("i:i"), 4, only(ModeApp)},
// Like load/store, but scratch slot taken from TOS instead of immediate
{0x3e, "loads", opLoads, proto("i:a"), 5, typed(typeLoads)},
@@ -526,31 +526,31 @@ var OpSpecs = []OpSpec{
{0x5e, "base64_decode", opBase64Decode, proto("b:b"), fidoVersion, field("e", &Base64Encodings).costByLength(1, 1, 16, 0)},
{0x5f, "json_ref", opJSONRef, proto("bb:a"), fidoVersion, field("r", &JSONRefTypes).costByLength(25, 2, 7, 1)},
- {0x60, "balance", opBalance, proto("i:i"), 2, only(modeApp)},
- {0x60, "balance", opBalance, proto("a:i"), directRefEnabledVersion, only(modeApp)},
- {0x61, "app_opted_in", opAppOptedIn, proto("ii:i"), 2, only(modeApp)},
- {0x61, "app_opted_in", opAppOptedIn, proto("ai:i"), directRefEnabledVersion, only(modeApp)},
- {0x62, "app_local_get", opAppLocalGet, proto("ib:a"), 2, only(modeApp)},
- {0x62, "app_local_get", opAppLocalGet, proto("ab:a"), directRefEnabledVersion, only(modeApp)},
- {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iib:ai"), 2, only(modeApp)},
- {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aib:ai"), directRefEnabledVersion, only(modeApp)},
- {0x64, "app_global_get", opAppGlobalGet, proto("b:a"), 2, only(modeApp)},
- {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("ib:ai"), 2, only(modeApp)},
- {0x66, "app_local_put", opAppLocalPut, proto("iba:"), 2, only(modeApp)},
- {0x66, "app_local_put", opAppLocalPut, proto("aba:"), directRefEnabledVersion, only(modeApp)},
- {0x67, "app_global_put", opAppGlobalPut, proto("ba:"), 2, only(modeApp)},
- {0x68, "app_local_del", opAppLocalDel, proto("ib:"), 2, only(modeApp)},
- {0x68, "app_local_del", opAppLocalDel, proto("ab:"), directRefEnabledVersion, only(modeApp)},
- {0x69, "app_global_del", opAppGlobalDel, proto("b:"), 2, only(modeApp)},
-
- {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:ai"), 2, field("f", &AssetHoldingFields).only(modeApp)},
- {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:ai"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(modeApp)},
- {0x71, "asset_params_get", opAssetParamsGet, proto("i:ai"), 2, field("f", &AssetParamsFields).only(modeApp)},
- {0x72, "app_params_get", opAppParamsGet, proto("i:ai"), 5, field("f", &AppParamsFields).only(modeApp)},
- {0x73, "acct_params_get", opAcctParamsGet, proto("a:ai"), 6, field("f", &AcctParamsFields).only(modeApp)},
-
- {0x78, "min_balance", opMinBalance, proto("i:i"), 3, only(modeApp)},
- {0x78, "min_balance", opMinBalance, proto("a:i"), directRefEnabledVersion, only(modeApp)},
+ {0x60, "balance", opBalance, proto("i:i"), 2, only(ModeApp)},
+ {0x60, "balance", opBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)},
+ {0x61, "app_opted_in", opAppOptedIn, proto("ii:i"), 2, only(ModeApp)},
+ {0x61, "app_opted_in", opAppOptedIn, proto("ai:i"), directRefEnabledVersion, only(ModeApp)},
+ {0x62, "app_local_get", opAppLocalGet, proto("ib:a"), 2, only(ModeApp)},
+ {0x62, "app_local_get", opAppLocalGet, proto("ab:a"), directRefEnabledVersion, only(ModeApp)},
+ {0x63, "app_local_get_ex", opAppLocalGetEx, proto("iib:ai"), 2, only(ModeApp)},
+ {0x63, "app_local_get_ex", opAppLocalGetEx, proto("aib:ai"), directRefEnabledVersion, only(ModeApp)},
+ {0x64, "app_global_get", opAppGlobalGet, proto("b:a"), 2, only(ModeApp)},
+ {0x65, "app_global_get_ex", opAppGlobalGetEx, proto("ib:ai"), 2, only(ModeApp)},
+ {0x66, "app_local_put", opAppLocalPut, proto("iba:"), 2, only(ModeApp)},
+ {0x66, "app_local_put", opAppLocalPut, proto("aba:"), directRefEnabledVersion, only(ModeApp)},
+ {0x67, "app_global_put", opAppGlobalPut, proto("ba:"), 2, only(ModeApp)},
+ {0x68, "app_local_del", opAppLocalDel, proto("ib:"), 2, only(ModeApp)},
+ {0x68, "app_local_del", opAppLocalDel, proto("ab:"), directRefEnabledVersion, only(ModeApp)},
+ {0x69, "app_global_del", opAppGlobalDel, proto("b:"), 2, only(ModeApp)},
+
+ {0x70, "asset_holding_get", opAssetHoldingGet, proto("ii:ai"), 2, field("f", &AssetHoldingFields).only(ModeApp)},
+ {0x70, "asset_holding_get", opAssetHoldingGet, proto("ai:ai"), directRefEnabledVersion, field("f", &AssetHoldingFields).only(ModeApp)},
+ {0x71, "asset_params_get", opAssetParamsGet, proto("i:ai"), 2, field("f", &AssetParamsFields).only(ModeApp)},
+ {0x72, "app_params_get", opAppParamsGet, proto("i:ai"), 5, field("f", &AppParamsFields).only(ModeApp)},
+ {0x73, "acct_params_get", opAcctParamsGet, proto("a:ai"), 6, field("f", &AcctParamsFields).only(ModeApp)},
+
+ {0x78, "min_balance", opMinBalance, proto("i:i"), 3, only(ModeApp)},
+ {0x78, "min_balance", opMinBalance, proto("a:i"), directRefEnabledVersion, only(ModeApp)},
// Immediate bytes and ints. Smaller code size for single use of constant.
{0x80, "pushbytes", opPushBytes, proto(":b"), 3, constants(asmPushBytes, opPushBytes, "bytes", immBytes)},
@@ -607,33 +607,33 @@ var OpSpecs = []OpSpec{
{0xaf, "bzero", opBytesZero, proto("i:b"), 4, detDefault()},
// AVM "effects"
- {0xb0, "log", opLog, proto("b:"), 5, only(modeApp)},
- {0xb1, "itxn_begin", opTxBegin, proto(":"), 5, only(modeApp)},
- {0xb2, "itxn_field", opItxnField, proto("a:"), 5, immediates("f").typed(typeTxField).field("f", &TxnFields).only(modeApp).assembler(asmItxnField)},
- {0xb3, "itxn_submit", opItxnSubmit, proto(":"), 5, only(modeApp)},
- {0xb4, "itxn", opItxn, proto(":a"), 5, field("f", &TxnScalarFields).only(modeApp).assembler(asmItxn)},
- {0xb5, "itxna", opItxna, proto(":a"), 5, immediates("f", "i").field("f", &TxnArrayFields).only(modeApp)},
- {0xb6, "itxn_next", opItxnNext, proto(":"), 6, only(modeApp)},
- {0xb7, "gitxn", opGitxn, proto(":a"), 6, immediates("t", "f").field("f", &TxnFields).only(modeApp).assembler(asmGitxn)},
- {0xb8, "gitxna", opGitxna, proto(":a"), 6, immediates("t", "f", "i").field("f", &TxnArrayFields).only(modeApp)},
+ {0xb0, "log", opLog, proto("b:"), 5, only(ModeApp)},
+ {0xb1, "itxn_begin", opTxBegin, proto(":"), 5, only(ModeApp)},
+ {0xb2, "itxn_field", opItxnField, proto("a:"), 5, immediates("f").typed(typeTxField).field("f", &TxnFields).only(ModeApp).assembler(asmItxnField)},
+ {0xb3, "itxn_submit", opItxnSubmit, proto(":"), 5, only(ModeApp)},
+ {0xb4, "itxn", opItxn, proto(":a"), 5, field("f", &TxnScalarFields).only(ModeApp).assembler(asmItxn)},
+ {0xb5, "itxna", opItxna, proto(":a"), 5, immediates("f", "i").field("f", &TxnArrayFields).only(ModeApp)},
+ {0xb6, "itxn_next", opItxnNext, proto(":"), 6, only(ModeApp)},
+ {0xb7, "gitxn", opGitxn, proto(":a"), 6, immediates("t", "f").field("f", &TxnFields).only(ModeApp).assembler(asmGitxn)},
+ {0xb8, "gitxna", opGitxna, proto(":a"), 6, immediates("t", "f", "i").field("f", &TxnArrayFields).only(ModeApp)},
// Unlimited Global Storage - Boxes
- {0xb9, "box_create", opBoxCreate, proto("bi:i"), boxVersion, only(modeApp)},
- {0xba, "box_extract", opBoxExtract, proto("bii:b"), boxVersion, only(modeApp)},
- {0xbb, "box_replace", opBoxReplace, proto("bib:"), boxVersion, only(modeApp)},
- {0xbc, "box_del", opBoxDel, proto("b:i"), boxVersion, only(modeApp)},
- {0xbd, "box_len", opBoxLen, proto("b:ii"), boxVersion, only(modeApp)},
- {0xbe, "box_get", opBoxGet, proto("b:bi"), boxVersion, only(modeApp)},
- {0xbf, "box_put", opBoxPut, proto("bb:"), boxVersion, only(modeApp)},
+ {0xb9, "box_create", opBoxCreate, proto("bi:i"), boxVersion, only(ModeApp)},
+ {0xba, "box_extract", opBoxExtract, proto("bii:b"), boxVersion, only(ModeApp)},
+ {0xbb, "box_replace", opBoxReplace, proto("bib:"), boxVersion, only(ModeApp)},
+ {0xbc, "box_del", opBoxDel, proto("b:i"), boxVersion, only(ModeApp)},
+ {0xbd, "box_len", opBoxLen, proto("b:ii"), boxVersion, only(ModeApp)},
+ {0xbe, "box_get", opBoxGet, proto("b:bi"), boxVersion, only(ModeApp)},
+ {0xbf, "box_put", opBoxPut, proto("bb:"), boxVersion, only(ModeApp)},
// Dynamic indexing
{0xc0, "txnas", opTxnas, proto("i:a"), 5, field("f", &TxnArrayFields)},
{0xc1, "gtxnas", opGtxnas, proto("i:a"), 5, immediates("t", "f").field("f", &TxnArrayFields)},
{0xc2, "gtxnsas", opGtxnsas, proto("ii:a"), 5, field("f", &TxnArrayFields)},
- {0xc3, "args", opArgs, proto("i:b"), 5, only(modeSig)},
- {0xc4, "gloadss", opGloadss, proto("ii:a"), 6, only(modeApp)},
- {0xc5, "itxnas", opItxnas, proto("i:a"), 6, field("f", &TxnArrayFields).only(modeApp)},
- {0xc6, "gitxnas", opGitxnas, proto("i:a"), 6, immediates("t", "f").field("f", &TxnArrayFields).only(modeApp)},
+ {0xc3, "args", opArgs, proto("i:b"), 5, only(ModeSig)},
+ {0xc4, "gloadss", opGloadss, proto("ii:a"), 6, only(ModeApp)},
+ {0xc5, "itxnas", opItxnas, proto("i:a"), 6, field("f", &TxnArrayFields).only(ModeApp)},
+ {0xc6, "gitxnas", opGitxnas, proto("i:a"), 6, immediates("t", "f").field("f", &TxnArrayFields).only(ModeApp)},
// randomness support
{0xd0, "vrf_verify", opVrfVerify, proto("bbb:bi"), randomnessVersion, field("s", &VrfStandards).costs(5700)},
diff --git a/data/transactions/logic/opcodes_test.go b/data/transactions/logic/opcodes_test.go
index 4acbc040e..2ce3648a6 100644
--- a/data/transactions/logic/opcodes_test.go
+++ b/data/transactions/logic/opcodes_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -58,7 +58,7 @@ func (os *OpSpec) equals(oso *OpSpec) bool {
return true
}
-func TestOpcodesByVersionReordered(t *testing.T) {
+func TestOpcodesByVersionReordered(t *testing.T) { // nolint:paralleltest // manipulates global OpSpecs
partitiontest.PartitionTest(t)
// Make a copy to restore to the original
@@ -82,6 +82,7 @@ func TestOpcodesByVersionReordered(t *testing.T) {
func TestOpcodesByVersion(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
testOpcodesByVersion(t)
}
diff --git a/data/transactions/logic/pairing.go b/data/transactions/logic/pairing.go
index 25dfea40a..315caac70 100644
--- a/data/transactions/logic/pairing.go
+++ b/data/transactions/logic/pairing.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/pairing_test.go b/data/transactions/logic/pairing_test.go
index 75f6e2bc2..c96bd6719 100644
--- a/data/transactions/logic/pairing_test.go
+++ b/data/transactions/logic/pairing_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/parsing.go b/data/transactions/logic/parsing.go
deleted file mode 100644
index 7a7429221..000000000
--- a/data/transactions/logic/parsing.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (C) 2019-2022 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 logic
-
-import (
- "encoding/base32"
- "encoding/base64"
- "encoding/binary"
- "fmt"
- "strconv"
- "strings"
-
- "github.com/algorand/avm-abi/abi"
- "github.com/algorand/go-algorand/data/basics"
-)
-
-// AppCallBytes represents an encoding and a value of an app call argument.
-type AppCallBytes struct {
- Encoding string `codec:"encoding"`
- Value string `codec:"value"`
-}
-
-// NewAppCallBytes parses an argument of the form "encoding:value" to AppCallBytes.
-func NewAppCallBytes(arg string) (AppCallBytes, error) {
- parts := strings.SplitN(arg, ":", 2)
- if len(parts) != 2 {
- return AppCallBytes{}, fmt.Errorf("all arguments and box names should be of the form 'encoding:value'")
- }
- return AppCallBytes{
- Encoding: parts[0],
- Value: parts[1],
- }, nil
-}
-
-// Raw converts an AppCallBytes arg to a byte array.
-func (arg AppCallBytes) Raw() (rawValue []byte, parseErr error) {
- switch arg.Encoding {
- case "str", "string":
- rawValue = []byte(arg.Value)
- case "int", "integer":
- num, err := strconv.ParseUint(arg.Value, 10, 64)
- if err != nil {
- parseErr = fmt.Errorf("Could not parse uint64 from string (%s): %v", arg.Value, err)
- return
- }
- ibytes := make([]byte, 8)
- binary.BigEndian.PutUint64(ibytes, num)
- rawValue = ibytes
- case "addr", "address":
- addr, err := basics.UnmarshalChecksumAddress(arg.Value)
- if err != nil {
- parseErr = fmt.Errorf("Could not unmarshal checksummed address from string (%s): %v", arg.Value, err)
- return
- }
- rawValue = addr[:]
- case "b32", "base32", "byte base32":
- data, err := base32.StdEncoding.DecodeString(arg.Value)
- if err != nil {
- parseErr = fmt.Errorf("Could not decode base32-encoded string (%s): %v", arg.Value, err)
- return
- }
- rawValue = data
- case "b64", "base64", "byte base64":
- data, err := base64.StdEncoding.DecodeString(arg.Value)
- if err != nil {
- parseErr = fmt.Errorf("Could not decode base64-encoded string (%s): %v", arg.Value, err)
- return
- }
- rawValue = data
- case "abi":
- typeAndValue := strings.SplitN(arg.Value, ":", 2)
- if len(typeAndValue) != 2 {
- parseErr = fmt.Errorf("Could not decode abi string (%s): should split abi-type and abi-value with colon", arg.Value)
- return
- }
- abiType, err := abi.TypeOf(typeAndValue[0])
- if err != nil {
- parseErr = fmt.Errorf("Could not decode abi type string (%s): %v", typeAndValue[0], err)
- return
- }
- value, err := abiType.UnmarshalFromJSON([]byte(typeAndValue[1]))
- if err != nil {
- parseErr = fmt.Errorf("Could not decode abi value string (%s):%v ", typeAndValue[1], err)
- return
- }
- return abiType.Encode(value)
- default:
- parseErr = fmt.Errorf("Unknown encoding: %s", arg.Encoding)
- }
- return
-}
diff --git a/data/transactions/logic/parsing_test.go b/data/transactions/logic/parsing_test.go
deleted file mode 100644
index 5bc3113b8..000000000
--- a/data/transactions/logic/parsing_test.go
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (C) 2019-2022 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 logic
-
-import (
- "encoding/base32"
- "encoding/base64"
- "encoding/binary"
- "fmt"
- "math"
- "testing"
-
- "github.com/algorand/avm-abi/abi"
- "github.com/algorand/go-algorand/data/basics"
-
- "github.com/algorand/go-algorand/test/partitiontest"
- "github.com/stretchr/testify/require"
-)
-
-func TestNewAppCallBytes(t *testing.T) {
- partitiontest.PartitionTest(t)
- t.Parallel()
-
- t.Run("errors", func(t *testing.T) {
- _, err := NewAppCallBytes("hello")
- require.Error(t, err)
-
- for _, v := range []string{":x", "int:-1"} {
- acb, err := NewAppCallBytes(v)
- _, err = acb.Raw()
- require.Error(t, err)
- }
- })
-
- for _, v := range []string{"hello", "1:2"} {
- for _, e := range []string{"str", "string"} {
- t.Run(fmt.Sprintf("encoding=%v,value=%v", e, v), func(t *testing.T) {
- acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, v))
- require.NoError(t, err)
- r, err := acb.Raw()
- require.NoError(t, err)
- require.Equal(t, v, string(r))
- })
- }
-
- for _, e := range []string{"b32", "base32", "byte base32"} {
- ve := base32.StdEncoding.EncodeToString([]byte(v))
- t.Run(fmt.Sprintf("encoding=%v,value=%v", e, ve), func(t *testing.T) {
- acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, ve))
- require.NoError(t, err)
- r, err := acb.Raw()
- require.NoError(t, err)
- require.Equal(t, ve, base32.StdEncoding.EncodeToString(r))
- })
- }
-
- for _, e := range []string{"b64", "base64", "byte base64"} {
- ve := base64.StdEncoding.EncodeToString([]byte(v))
- t.Run(fmt.Sprintf("encoding=%v,value=%v", e, ve), func(t *testing.T) {
- acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, ve))
- require.NoError(t, err)
- r, err := acb.Raw()
- require.NoError(t, err)
- require.Equal(t, ve, base64.StdEncoding.EncodeToString(r))
- })
- }
- }
-
- for _, v := range []uint64{1, 0, math.MaxUint64} {
- for _, e := range []string{"int", "integer"} {
- t.Run(fmt.Sprintf("encoding=%v,value=%v", e, v), func(t *testing.T) {
- acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, v))
- require.NoError(t, err)
- r, err := acb.Raw()
- require.NoError(t, err)
- require.Equal(t, v, binary.BigEndian.Uint64(r))
- })
- }
- }
-
- for _, v := range []string{"737777777777777777777777777777777777777777777777777UFEJ2CI"} {
- for _, e := range []string{"addr", "address"} {
- t.Run(fmt.Sprintf("encoding=%v,value=%v", e, v), func(t *testing.T) {
- acb, err := NewAppCallBytes(fmt.Sprintf("%v:%v", e, v))
- require.NoError(t, err)
- r, err := acb.Raw()
- require.NoError(t, err)
- addr, err := basics.UnmarshalChecksumAddress(v)
- require.NoError(t, err)
- expectedBytes := []byte{}
- expectedBytes = addr[:]
- require.Equal(t, expectedBytes, r)
- })
- }
- }
-
- type abiCase struct {
- abiType, rawValue string
- }
- for _, v := range []abiCase{
- {
- `(uint64,string,bool[])`,
- `[399,"should pass",[true,false,false,true]]`,
- }} {
- for _, e := range []string{"abi"} {
- t.Run(fmt.Sprintf("encoding=%v,value=%v", e, v), func(t *testing.T) {
- acb, err := NewAppCallBytes(fmt.Sprintf(
- "%v:%v:%v", e, v.abiType, v.rawValue))
- require.NoError(t, err)
- r, err := acb.Raw()
- require.NoError(t, err)
- require.NotEmpty(t, r)
-
- // Confirm round-trip works.
- abiType, err := abi.TypeOf(v.abiType)
- require.NoError(t, err)
- d, err := abiType.Decode(r)
- require.NoError(t, err)
- vv, err := abiType.Encode(d)
- require.NoError(t, err)
- require.Equal(t, r, vv)
- })
- }
- }
-}
diff --git a/data/transactions/logic/program.go b/data/transactions/logic/program.go
index 58265b7f9..4568ebe74 100644
--- a/data/transactions/logic/program.go
+++ b/data/transactions/logic/program.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/sourcemap.go b/data/transactions/logic/sourcemap.go
index b2e6e2acc..3580a120e 100644
--- a/data/transactions/logic/sourcemap.go
+++ b/data/transactions/logic/sourcemap.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/logic/sourcemap_test.go b/data/transactions/logic/sourcemap_test.go
index 7407f39dd..c3b1a73a8 100644
--- a/data/transactions/logic/sourcemap_test.go
+++ b/data/transactions/logic/sourcemap_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -26,6 +26,7 @@ import (
func TestGetSourceMap(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
a := require.New(t)
sourceNames := []string{"test.teal"}
@@ -55,6 +56,7 @@ func TestGetSourceMap(t *testing.T) {
func TestVLQ(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
a := require.New(t)
a.Equal("AAAA", MakeSourceMapLine(0, 0, 0, 0))
diff --git a/data/transactions/logic/tracer.go b/data/transactions/logic/tracer.go
new file mode 100644
index 000000000..929d5cdb8
--- /dev/null
+++ b/data/transactions/logic/tracer.go
@@ -0,0 +1,160 @@
+// 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 logic
+
+import "github.com/algorand/go-algorand/data/transactions"
+
+// EvalTracer functions are called by eval function during AVM program execution, if a tracer
+// is provided.
+//
+// Refer to the lifecycle graph below for the sequence in which hooks are called.
+//
+// NOTE: Arguments given to Tracer hooks (EvalParams and EvalContext) are passed by reference,
+// they are not copies. It is therefore the responsibility of the tracer implementation to NOT
+// modify the state of the structs passed to them. Additionally, hooks are responsible for copying
+// the information they need from the argument structs. No guarantees are made that the referenced
+// state will not change between hook calls. This decision was made in an effort to reduce the
+// performance impact of tracers.
+//
+// LOGICSIG LIFECYCLE GRAPH
+// ┌─────────────────────────┐
+// │ LogicSig Evaluation │
+// ├─────────────────────────┤
+// │ > BeforeProgram │
+// │ │
+// │ ┌───────────────────┐ │
+// │ │ Teal Operation │ │
+// │ ├───────────────────┤ │
+// │ │ > BeforeOpcode │ │
+// │ │ │ │
+// │ │ > AfterOpcode │ │
+// │ └───────────────────┘ │
+// | ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ │
+// │ │
+// │ > AfterProgram │
+// └─────────────────────────┘
+//
+// APP LIFECYCLE GRAPH
+// ┌──────────────────────────────────────────────────────┐
+// │ Transaction Evaluation │
+// ├──────────────────────────────────────────────────────┤
+// │ > BeforeTxnGroup │
+// │ │
+// │ ┌────────────────────────────────────────────────┐ │
+// │ │ > BeforeTxn │ │
+// │ │ │ │
+// │ │ ┌──────────────────────────────────────────┐ │ │
+// │ │ │ ? App Call │ │ │
+// │ │ ├──────────────────────────────────────────┤ │ │
+// │ │ │ > BeforeProgram │ │ │
+// │ │ │ │ │ │
+// │ │ │ ┌────────────────────────────────────┐ │ │ │
+// │ │ │ │ Teal Operation │ │ │ │
+// │ │ │ ├────────────────────────────────────┤ │ │ │
+// │ │ │ │ > BeforeOpcode │ │ │ │
+// │ │ │ │ ┌──────────────────────────────┐ │ │ │ │
+// │ │ │ │ │ ? Inner Transaction Group │ │ │ │ │
+// │ │ │ │ ├──────────────────────────────┤ │ │ │ │
+// │ │ │ │ │ > BeforeTxnGroup │ │ │ │ │
+// │ │ │ │ │ ┌────────────────────────┐ │ │ │ │ │
+// │ │ │ │ │ │ Transaction Evaluation │ │ │ │ │ │
+// │ │ │ │ │ ├────────────────────────┤ │ │ │ │ │
+// │ │ │ │ │ │ ... │ │ │ │ │ │
+// │ │ │ │ │ └────────────────────────┘ │ │ │ │ │
+// │ │ │ │ │ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ │ │ │ │ │
+// │ │ │ │ │ │ │ │ │ │
+// │ │ │ │ │ > AfterTxnGroup │ │ │ │ │
+// │ │ │ │ └──────────────────────────────┘ │ │ │ │
+// │ │ │ │ > AfterOpcode │ │ │ │
+// │ │ │ └────────────────────────────────────┘ │ │ │
+// │ │ │ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ │ │ │
+// │ │ │ │ │ │
+// │ │ │ > AfterProgram │ │ │
+// │ │ └──────────────────────────────────────────┘ │ │
+// | | ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ │ |
+// │ │ │ │
+// │ │ > AfterTxn │ │
+// │ └────────────────────────────────────────────────┘ │
+// | ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ ⁞ |
+// │ │
+// │ > AfterTxnGroup │
+// └──────────────────────────────────────────────────────┘
+type EvalTracer interface {
+ // BeforeTxnGroup is called before a transaction group is executed. This includes both top-level
+ // and inner transaction groups. The argument ep is the EvalParams object for the group; if the
+ // group is an inner group, this is the EvalParams object for the inner group.
+ //
+ // Each transaction within the group calls BeforeTxn and subsequent hooks, as described in the
+ // lifecycle diagram.
+ BeforeTxnGroup(ep *EvalParams)
+
+ // AfterTxnGroup is called after a transaction group has been executed. This includes both
+ // top-level and inner transaction groups. The argument ep is the EvalParams object for the
+ // group; if the group is an inner group, this is the EvalParams object for the inner group.
+ AfterTxnGroup(ep *EvalParams)
+
+ // BeforeTxn is called before a transaction is executed.
+ //
+ // groupIndex refers to the index of the transaction in the transaction group that will be executed.
+ BeforeTxn(ep *EvalParams, groupIndex int)
+
+ // AfterTxn is called after a transaction has been executed.
+ //
+ // groupIndex refers to the index of the transaction in the transaction group that was just executed.
+ // ad is the ApplyData result of the transaction; prefer using this instead of
+ // ep.TxnGroup[groupIndex].ApplyData, since it may not be populated at this point.
+ AfterTxn(ep *EvalParams, groupIndex int, ad transactions.ApplyData)
+
+ // BeforeProgram is called before an app or LogicSig program is evaluated.
+ BeforeProgram(cx *EvalContext)
+
+ // AfterProgram is called after an app or LogicSig program is evaluated.
+ AfterProgram(cx *EvalContext, evalError error)
+
+ // BeforeOpcode is called before the op is evaluated
+ BeforeOpcode(cx *EvalContext)
+
+ // AfterOpcode is called after the op has been evaluated
+ AfterOpcode(cx *EvalContext, evalError error)
+}
+
+// NullEvalTracer implements EvalTracer, but all of its hook methods do nothing
+type NullEvalTracer struct{}
+
+// BeforeTxnGroup does nothing
+func (n NullEvalTracer) BeforeTxnGroup(ep *EvalParams) {}
+
+// AfterTxnGroup does nothing
+func (n NullEvalTracer) AfterTxnGroup(ep *EvalParams) {}
+
+// BeforeTxn does nothing
+func (n NullEvalTracer) BeforeTxn(ep *EvalParams, groupIndex int) {}
+
+// AfterTxn does nothing
+func (n NullEvalTracer) AfterTxn(ep *EvalParams, groupIndex int, ad transactions.ApplyData) {}
+
+// BeforeProgram does nothing
+func (n NullEvalTracer) BeforeProgram(cx *EvalContext) {}
+
+// AfterProgram does nothing
+func (n NullEvalTracer) AfterProgram(cx *EvalContext, evalError error) {}
+
+// BeforeOpcode does nothing
+func (n NullEvalTracer) BeforeOpcode(cx *EvalContext) {}
+
+// AfterOpcode does nothing
+func (n NullEvalTracer) AfterOpcode(cx *EvalContext, evalError error) {}
diff --git a/data/transactions/logic/tracer_test.go b/data/transactions/logic/tracer_test.go
new file mode 100644
index 000000000..c47f3d192
--- /dev/null
+++ b/data/transactions/logic/tracer_test.go
@@ -0,0 +1,195 @@
+// 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 logic
+
+import (
+ "testing"
+
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/data/transactions"
+ "github.com/algorand/go-algorand/test/partitiontest"
+ "github.com/stretchr/testify/require"
+)
+
+const innerTxnTestProgram string = `itxn_begin
+int appl
+itxn_field TypeEnum
+int NoOp
+itxn_field OnCompletion
+byte 0x068101 // #pragma version 6; int 1;
+dup
+itxn_field ApprovalProgram
+itxn_field ClearStateProgram
+itxn_submit
+
+itxn_begin
+int pay
+itxn_field TypeEnum
+int 1
+itxn_field Amount
+global CurrentApplicationAddress
+itxn_field Receiver
+itxn_next
+int pay
+itxn_field TypeEnum
+int 2
+itxn_field Amount
+global CurrentApplicationAddress
+itxn_field Receiver
+itxn_submit
+
+int 1
+`
+
+// can't use mocktracer.Tracer because the import would be circular
+type testEvalTracer struct {
+ beforeTxnGroupCalls int
+ afterTxnGroupCalls int
+
+ beforeTxnCalls int
+ afterTxnCalls int
+
+ beforeProgramCalls int
+ afterProgramCalls int
+ programModes []RunMode
+
+ beforeOpcodeCalls int
+ afterOpcodeCalls int
+}
+
+func (t *testEvalTracer) BeforeTxnGroup(ep *EvalParams) {
+ t.beforeTxnGroupCalls++
+}
+
+func (t *testEvalTracer) AfterTxnGroup(ep *EvalParams) {
+ t.afterTxnGroupCalls++
+}
+
+func (t *testEvalTracer) BeforeTxn(ep *EvalParams, groupIndex int) {
+ t.beforeTxnCalls++
+}
+
+func (t *testEvalTracer) AfterTxn(ep *EvalParams, groupIndex int, ad transactions.ApplyData) {
+ t.afterTxnCalls++
+}
+
+func (t *testEvalTracer) BeforeProgram(cx *EvalContext) {
+ t.beforeProgramCalls++
+ t.programModes = append(t.programModes, cx.RunMode())
+}
+
+func (t *testEvalTracer) AfterProgram(cx *EvalContext, evalError error) {
+ t.afterProgramCalls++
+}
+
+func (t *testEvalTracer) BeforeOpcode(cx *EvalContext) {
+ t.beforeOpcodeCalls++
+}
+
+func (t *testEvalTracer) AfterOpcode(cx *EvalContext, evalError error) {
+ t.afterOpcodeCalls++
+}
+
+func TestEvalWithTracer(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ t.Run("logicsig", func(t *testing.T) {
+ t.Parallel()
+ testTracer := testEvalTracer{}
+ ep := defaultEvalParams()
+ ep.Tracer = &testTracer
+ testLogic(t, debuggerTestProgram, AssemblerMaxVersion, ep)
+
+ // BeforeTxnGroup/AfterTxnGroup/BeforeTxn/AfterTxn are only called for the inner txns in
+ // this test, not the top-level ones
+ require.Zero(t, testTracer.beforeTxnGroupCalls)
+ require.Zero(t, testTracer.afterTxnGroupCalls)
+ require.Zero(t, testTracer.beforeTxnCalls)
+ require.Zero(t, testTracer.afterTxnCalls)
+
+ require.Equal(t, 1, testTracer.beforeProgramCalls)
+ require.Equal(t, 1, testTracer.afterProgramCalls)
+ require.Equal(t, []RunMode{ModeSig}, testTracer.programModes)
+
+ require.Equal(t, 35, testTracer.beforeOpcodeCalls)
+ require.Equal(t, testTracer.beforeOpcodeCalls, testTracer.afterOpcodeCalls)
+ })
+
+ t.Run("simple app", func(t *testing.T) {
+ t.Parallel()
+ testTracer := testEvalTracer{}
+ ep := defaultEvalParams()
+ ep.Tracer = &testTracer
+ testApp(t, debuggerTestProgram, ep)
+
+ // BeforeTxnGroup/AfterTxnGroup/BeforeTxn/AfterTxn are only called for the inner txns in
+ // this test, not the top-level ones
+ require.Zero(t, testTracer.beforeTxnGroupCalls)
+ require.Zero(t, testTracer.afterTxnGroupCalls)
+ require.Zero(t, testTracer.beforeTxnCalls)
+ require.Zero(t, testTracer.afterTxnCalls)
+
+ require.Equal(t, 1, testTracer.beforeProgramCalls)
+ require.Equal(t, 1, testTracer.afterProgramCalls)
+ require.Equal(t, []RunMode{ModeApp}, testTracer.programModes)
+
+ require.Equal(t, 35, testTracer.beforeOpcodeCalls)
+ require.Equal(t, testTracer.beforeOpcodeCalls, testTracer.afterOpcodeCalls)
+ })
+
+ t.Run("app with inner txns", func(t *testing.T) {
+ t.Parallel()
+ testTracer := testEvalTracer{}
+ ep, tx, ledger := MakeSampleEnv()
+
+ // Establish 888 as the app id, and fund it.
+ ledger.NewApp(tx.Receiver, 888, basics.AppParams{})
+ ledger.NewAccount(basics.AppIndex(888).Address(), 200000)
+
+ ep.Tracer = &testTracer
+ testApp(t, innerTxnTestProgram, ep)
+
+ // BeforeTxnGroup/AfterTxnGroup/BeforeTxn/AfterTxn are only called for the inner txns in
+ // this test, not the top-level ones
+
+ // two groups of inner txns were issued
+ require.Equal(t, 2, testTracer.beforeTxnGroupCalls)
+ require.Equal(t, 2, testTracer.afterTxnGroupCalls)
+
+ // three total inner txns were issued
+ require.Equal(t, 3, testTracer.beforeTxnCalls)
+ require.Equal(t, 3, testTracer.afterTxnCalls)
+
+ require.Equal(t, 2, testTracer.beforeProgramCalls)
+ require.Equal(t, 2, testTracer.afterProgramCalls)
+ require.Equal(t, []RunMode{ModeApp, ModeApp}, testTracer.programModes)
+
+ appCallTealOps := 27
+ innerAppCallTealOps := 1
+ require.Equal(t, appCallTealOps+innerAppCallTealOps, testTracer.beforeOpcodeCalls)
+ require.Equal(t, testTracer.beforeOpcodeCalls, testTracer.afterOpcodeCalls)
+ })
+}
+
+func TestNullEvalTracerIsEvalTracer(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ var tracer EvalTracer = NullEvalTracer{}
+ require.NotNil(t, tracer)
+}
diff --git a/data/transactions/logicsig.go b/data/transactions/logicsig.go
index 03a520fc6..e2869a89b 100644
--- a/data/transactions/logicsig.go
+++ b/data/transactions/logicsig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/payment.go b/data/transactions/payment.go
index 7676315a3..fee9c395e 100644
--- a/data/transactions/payment.go
+++ b/data/transactions/payment.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/payment_test.go b/data/transactions/payment_test.go
index 7f88c7e79..5d3f83cba 100644
--- a/data/transactions/payment_test.go
+++ b/data/transactions/payment_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/payset.go b/data/transactions/payset.go
index c75755788..f005c6b4d 100644
--- a/data/transactions/payset.go
+++ b/data/transactions/payset.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/payset_test.go b/data/transactions/payset_test.go
index 9446f45b6..63d3afc42 100644
--- a/data/transactions/payset_test.go
+++ b/data/transactions/payset_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/perf_test.go b/data/transactions/perf_test.go
index 1aa0bed49..cda1ea5be 100644
--- a/data/transactions/perf_test.go
+++ b/data/transactions/perf_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/signedtxn.go b/data/transactions/signedtxn.go
index 08ce6fad9..4f0823d7f 100644
--- a/data/transactions/signedtxn.go
+++ b/data/transactions/signedtxn.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/signedtxn_test.go b/data/transactions/signedtxn_test.go
index 92de12182..d292e6689 100644
--- a/data/transactions/signedtxn_test.go
+++ b/data/transactions/signedtxn_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/sort.go b/data/transactions/sort.go
index f0f10286f..3ca8e4a56 100644
--- a/data/transactions/sort.go
+++ b/data/transactions/sort.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/stateproof.go b/data/transactions/stateproof.go
index 35ffe0ff1..4bc7c9a24 100644
--- a/data/transactions/stateproof.go
+++ b/data/transactions/stateproof.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/teal.go b/data/transactions/teal.go
index 3851a03e2..9472fd3b9 100644
--- a/data/transactions/teal.go
+++ b/data/transactions/teal.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/teal_test.go b/data/transactions/teal_test.go
index e5920d0f1..52ec80e68 100644
--- a/data/transactions/teal_test.go
+++ b/data/transactions/teal_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/testhelpers.go b/data/transactions/testhelpers.go
index 8c981791c..99baadb1a 100644
--- a/data/transactions/testhelpers.go
+++ b/data/transactions/testhelpers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/transaction.go b/data/transactions/transaction.go
index e0d908fd6..6c1b56fd2 100644
--- a/data/transactions/transaction.go
+++ b/data/transactions/transaction.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/transaction_test.go b/data/transactions/transaction_test.go
index 43467d3fd..51f69ce71 100644
--- a/data/transactions/transaction_test.go
+++ b/data/transactions/transaction_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/data/transactions/verify/artifact_test.go b/data/transactions/verify/artifact_test.go
index 8444dfa35..226ac5449 100644
--- a/data/transactions/verify/artifact_test.go
+++ b/data/transactions/verify/artifact_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -77,7 +77,7 @@ func BenchmarkTinyMan(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
- _, err := TxnGroup(stxnss[i], hdr, nil, &logic.NoHeaderLedger{})
+ _, err := TxnGroup(stxnss[i], &hdr, nil, &logic.NoHeaderLedger{})
require.NoError(b, err)
}
})
@@ -93,7 +93,7 @@ func BenchmarkTinyMan(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
- _, err := TxnGroup(stxns, hdr, nil, &logic.NoHeaderLedger{})
+ _, err := TxnGroup(stxns, &hdr, nil, &logic.NoHeaderLedger{})
require.NoError(b, err)
}
})
diff --git a/data/transactions/verify/txn.go b/data/transactions/verify/txn.go
index a4fe2ebd4..2edd69aa0 100644
--- a/data/transactions/verify/txn.go
+++ b/data/transactions/verify/txn.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -21,6 +21,9 @@ import (
"encoding/binary"
"errors"
"fmt"
+ "sync"
+ "sync/atomic"
+ "time"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
@@ -28,6 +31,8 @@ import (
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
+ "github.com/algorand/go-algorand/ledger/ledgercore"
+ "github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/util/execpool"
"github.com/algorand/go-algorand/util/metrics"
@@ -44,6 +49,8 @@ var msigLsigLessOrEqual4 = metrics.MakeCounter(metrics.MetricName{Name: "algod_v
var msigLsigLessOrEqual10 = metrics.MakeCounter(metrics.MetricName{Name: "algod_verify_msig_lsig_5_10", Description: "Total transaction scripts with 5-10 msigs"})
var msigLsigMore10 = metrics.MakeCounter(metrics.MetricName{Name: "algod_verify_msig_lsig_10", Description: "Total transaction scripts with 11+ msigs"})
+var errShuttingDownError = errors.New("not verified, verifier is shutting down")
+
// The PaysetGroups is taking large set of transaction groups and attempt to verify their validity using multiple go-routines.
// When doing so, it attempts to break these into smaller "worksets" where each workset takes about 2ms of execution time in order
// to avoid context switching overhead while providing good validation cancellation responsiveness. Each one of these worksets is
@@ -51,6 +58,19 @@ var msigLsigMore10 = metrics.MakeCounter(metrics.MetricName{Name: "algod_verify_
// show that these are realistic numbers )
const txnPerWorksetThreshold = 32
+// batchSizeBlockLimit is the limit when the batch exceeds, will be added to the exec pool, even if the pool is saturated
+// and the batch verifier will block until the exec pool accepts the batch
+const batchSizeBlockLimit = 1024
+
+// waitForNextTxnDuration is the time to wait before sending the batch to the exec pool
+// If the incoming txn rate is low, a txn in the batch may wait no less than
+// waitForNextTxnDuration before it is set for verification.
+// This can introduce a latency to the propagation of a transaction in the network,
+// since every relay will go through this wait time before broadcasting the txn.
+// However, when the incoming txn rate is high, the batch will fill up quickly and will send
+// for signature evaluation before waitForNextTxnDuration.
+const waitForNextTxnDuration = 2 * time.Millisecond
+
// When the PaysetGroups is generating worksets, it enqueues up to concurrentWorksets entries to the execution pool. This serves several
// purposes :
// - if the verification task need to be aborted, there are only concurrentWorksets entries that are currently redundant on the execution pool queue.
@@ -124,7 +144,7 @@ func (e *TxGroupError) Unwrap() error {
// PrepareGroupContext prepares a verification group parameter object for a given transaction
// group.
-func PrepareGroupContext(group []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader, ledger logic.LedgerForSignature) (*GroupContext, error) {
+func PrepareGroupContext(group []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, ledger logic.LedgerForSignature) (*GroupContext, error) {
if len(group) == 0 {
return nil, nil
}
@@ -155,7 +175,7 @@ func (g *GroupContext) Equal(other *GroupContext) bool {
// txnBatchPrep verifies a SignedTxn having no obviously inconsistent data.
// Block-assembly time checks of LogicSig and accounting rules may still block the txn.
// It is the caller responsibility to call batchVerifier.Verify().
-func txnBatchPrep(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, verifier *crypto.BatchVerifier) *TxGroupError {
+func txnBatchPrep(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, verifier *crypto.BatchVerifier, evalTracer logic.EvalTracer) *TxGroupError {
if !groupCtx.consensusParams.SupportRekeying && (s.AuthAddr != basics.Address{}) {
return &TxGroupError{err: errRekeyingNotSupported, Reason: TxGroupErrorReasonGeneric}
}
@@ -164,14 +184,23 @@ func txnBatchPrep(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext,
return &TxGroupError{err: err, Reason: TxGroupErrorReasonNotWellFormed}
}
- return stxnCoreChecks(s, txnIdx, groupCtx, verifier)
+ return stxnCoreChecks(s, txnIdx, groupCtx, verifier, evalTracer)
}
// TxnGroup verifies a []SignedTxn as being signed and having no obviously inconsistent data.
-func TxnGroup(stxs []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader, cache VerifiedTransactionCache, ledger logic.LedgerForSignature) (groupCtx *GroupContext, err error) {
+func TxnGroup(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, cache VerifiedTransactionCache, ledger logic.LedgerForSignature) (groupCtx *GroupContext, err error) {
+ return txnGroup(stxs, contextHdr, cache, ledger, nil)
+}
+
+// TxnGroupWithTracer verifies a []SignedTxn as being signed and having no obviously inconsistent data, while using a tracer.
+func TxnGroupWithTracer(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, cache VerifiedTransactionCache, ledger logic.LedgerForSignature, evalTracer logic.EvalTracer) (groupCtx *GroupContext, err error) {
+ return txnGroup(stxs, contextHdr, cache, ledger, evalTracer)
+}
+
+func txnGroup(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, cache VerifiedTransactionCache, ledger logic.LedgerForSignature, evalTracer logic.EvalTracer) (groupCtx *GroupContext, err error) {
batchVerifier := crypto.MakeBatchVerifier()
- if groupCtx, err = txnGroupBatchPrep(stxs, contextHdr, ledger, batchVerifier); err != nil {
+ if groupCtx, err = txnGroupBatchPrep(stxs, contextHdr, ledger, batchVerifier, evalTracer); err != nil {
return nil, err
}
@@ -188,7 +217,7 @@ func TxnGroup(stxs []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader,
// txnGroupBatchPrep verifies a []SignedTxn having no obviously inconsistent data.
// it is the caller responsibility to call batchVerifier.Verify()
-func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr bookkeeping.BlockHeader, ledger logic.LedgerForSignature, verifier *crypto.BatchVerifier) (*GroupContext, error) {
+func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, ledger logic.LedgerForSignature, verifier *crypto.BatchVerifier, evalTracer logic.EvalTracer) (*GroupContext, error) {
groupCtx, err := PrepareGroupContext(stxs, contextHdr, ledger)
if err != nil {
return nil, err
@@ -197,7 +226,7 @@ func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr bookkeeping.Blo
minFeeCount := uint64(0)
feesPaid := uint64(0)
for i, stxn := range stxs {
- prepErr := txnBatchPrep(&stxn, i, groupCtx, verifier)
+ prepErr := txnBatchPrep(&stxn, i, groupCtx, verifier, evalTracer)
if prepErr != nil {
// re-wrap the error with more details
prepErr.err = fmt.Errorf("transaction %+v invalid : %w", stxn, prepErr.err)
@@ -229,43 +258,56 @@ func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr bookkeeping.Blo
return groupCtx, nil
}
-// stxnCoreChecks runs signatures validity checks and enqueues signature into batchVerifier for verification.
-func stxnCoreChecks(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) *TxGroupError {
- numSigs := 0
- hasSig := false
- hasMsig := false
- hasLogicSig := false
+type sigOrTxnType int
+
+const regularSig sigOrTxnType = 1
+const multiSig sigOrTxnType = 2
+const logicSig sigOrTxnType = 3
+const stateProofTxn sigOrTxnType = 4
+
+// checkTxnSigTypeCounts checks the number of signature types and reports an error in case of a violation
+func checkTxnSigTypeCounts(s *transactions.SignedTxn) (sigType sigOrTxnType, err *TxGroupError) {
+ numSigCategories := 0
if s.Sig != (crypto.Signature{}) {
- numSigs++
- hasSig = true
+ numSigCategories++
+ sigType = regularSig
}
if !s.Msig.Blank() {
- numSigs++
- hasMsig = true
+ numSigCategories++
+ sigType = multiSig
}
if !s.Lsig.Blank() {
- numSigs++
- hasLogicSig = true
+ numSigCategories++
+ sigType = logicSig
}
- if numSigs == 0 {
+ if numSigCategories == 0 {
// Special case: special sender address can issue special transaction
// types (state proof txn) without any signature. The well-formed
// check ensures that this transaction cannot pay any fee, and
// cannot have any other interesting fields, except for the state proof payload.
if s.Txn.Sender == transactions.StateProofSender && s.Txn.Type == protocol.StateProofTx {
- return nil
+ return stateProofTxn, nil
}
- return &TxGroupError{err: errTxnSigHasNoSig, Reason: TxGroupErrorReasonHasNoSig}
+ return 0, &TxGroupError{err: errTxnSigHasNoSig, Reason: TxGroupErrorReasonHasNoSig}
}
- if numSigs > 1 {
- return &TxGroupError{err: errTxnSigNotWellFormed, Reason: TxGroupErrorReasonSigNotWellFormed}
+ if numSigCategories > 1 {
+ return 0, &TxGroupError{err: errTxnSigNotWellFormed, Reason: TxGroupErrorReasonSigNotWellFormed}
}
+ return sigType, nil
+}
- if hasSig {
+// stxnCoreChecks runs signatures validity checks and enqueues signature into batchVerifier for verification.
+func stxnCoreChecks(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier, evalTracer logic.EvalTracer) *TxGroupError {
+ sigType, err := checkTxnSigTypeCounts(s)
+ if err != nil {
+ return err
+ }
+
+ switch sigType {
+ case regularSig:
batchVerifier.EnqueueSignature(crypto.SignatureVerifier(s.Authorizer()), s.Txn, s.Sig)
return nil
- }
- if hasMsig {
+ case multiSig:
if err := crypto.MultisigBatchPrep(s.Txn, crypto.Digest(s.Authorizer()), s.Msig, batchVerifier); err != nil {
return &TxGroupError{err: fmt.Errorf("multisig validation failed: %w", err), Reason: TxGroupErrorReasonMsigNotWellFormed}
}
@@ -283,14 +325,19 @@ func stxnCoreChecks(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContex
msigMore10.Inc(nil)
}
return nil
- }
- if hasLogicSig {
- if err := logicSigVerify(s, txnIdx, groupCtx); err != nil {
+
+ case logicSig:
+ if err := logicSigVerify(s, txnIdx, groupCtx, evalTracer); err != nil {
return &TxGroupError{err: err, Reason: TxGroupErrorReasonLogicSigFailed}
}
return nil
+
+ case stateProofTxn:
+ return nil
+
+ default:
+ return &TxGroupError{err: errUnknownSignature, Reason: TxGroupErrorReasonGeneric}
}
- return &TxGroupError{err: errUnknownSignature, Reason: TxGroupErrorReasonGeneric}
}
// LogicSigSanityCheck checks that the signature is valid and that the program is basically well formed.
@@ -390,7 +437,7 @@ func logicSigSanityCheckBatchPrep(txn *transactions.SignedTxn, groupIndex int, g
}
// logicSigVerify checks that the signature is valid, executing the program.
-func logicSigVerify(txn *transactions.SignedTxn, groupIndex int, groupCtx *GroupContext) error {
+func logicSigVerify(txn *transactions.SignedTxn, groupIndex int, groupCtx *GroupContext, evalTracer logic.EvalTracer) error {
err := LogicSigSanityCheck(txn, groupIndex, groupCtx)
if err != nil {
return err
@@ -404,6 +451,7 @@ func logicSigVerify(txn *transactions.SignedTxn, groupIndex int, groupCtx *Group
TxnGroup: transactions.WrapSignedTxnsWithAD(groupCtx.signedGroupTxns),
MinAvmVersion: &groupCtx.minAvmVersion,
SigLedger: groupCtx.ledger,
+ Tracer: evalTracer,
}
pass, cx, err := logic.EvalSignatureFull(groupIndex, &ep)
if err != nil {
@@ -463,7 +511,7 @@ func PaysetGroups(ctx context.Context, payset [][]transactions.SignedTxn, blkHea
batchVerifier := crypto.MakeBatchVerifierWithHint(len(payset))
for i, signTxnsGrp := range txnGroups {
- groupCtxs[i], grpErr = txnGroupBatchPrep(signTxnsGrp, blkHeader, ledger, batchVerifier)
+ groupCtxs[i], grpErr = txnGroupBatchPrep(signTxnsGrp, &blkHeader, ledger, batchVerifier, nil)
// abort only if it's a non-cache error.
if grpErr != nil {
return grpErr
@@ -536,3 +584,377 @@ func (w *worksetBuilder) next() (txnGroups [][]transactions.SignedTxn) {
func (w *worksetBuilder) completed() bool {
return w.idx >= len(w.payset)
}
+
+// UnverifiedElement is the element passed to the Stream verifier
+// BacklogMessage is a *txBacklogMsg from data/txHandler.go which needs to be
+// passed back to that context
+type UnverifiedElement struct {
+ TxnGroup []transactions.SignedTxn
+ BacklogMessage interface{}
+}
+
+// VerificationResult is the result of the txn group verification
+// BacklogMessage is the reference associated with the txn group which was
+// initially passed to the stream verifier
+type VerificationResult struct {
+ TxnGroup []transactions.SignedTxn
+ BacklogMessage interface{}
+ Err error
+}
+
+// StreamVerifier verifies txn groups received through the stxnChan channel, and returns the
+// results through the resultChan
+type StreamVerifier struct {
+ resultChan chan<- *VerificationResult
+ droppedChan chan<- *UnverifiedElement
+ stxnChan <-chan *UnverifiedElement
+ verificationPool execpool.BacklogPool
+ ctx context.Context
+ cache VerifiedTransactionCache
+ activeLoopWg sync.WaitGroup
+ nbw *NewBlockWatcher
+ ledger logic.LedgerForSignature
+}
+
+// NewBlockWatcher is a struct used to provide a new block header to the
+// stream verifier
+type NewBlockWatcher struct {
+ blkHeader atomic.Value
+}
+
+// MakeNewBlockWatcher construct a new block watcher with the initial blkHdr
+func MakeNewBlockWatcher(blkHdr bookkeeping.BlockHeader) (nbw *NewBlockWatcher) {
+ nbw = &NewBlockWatcher{}
+ nbw.blkHeader.Store(&blkHdr)
+ return nbw
+}
+
+// OnNewBlock implements the interface to subscribe to new block notifications from the ledger
+func (nbw *NewBlockWatcher) OnNewBlock(block bookkeeping.Block, delta ledgercore.StateDelta) {
+ bh := nbw.blkHeader.Load().(*bookkeeping.BlockHeader)
+ if bh.Round >= block.BlockHeader.Round {
+ return
+ }
+ nbw.blkHeader.Store(&block.BlockHeader)
+}
+
+func (nbw *NewBlockWatcher) getBlockHeader() (bh *bookkeeping.BlockHeader) {
+ return nbw.blkHeader.Load().(*bookkeeping.BlockHeader)
+}
+
+type batchLoad struct {
+ txnGroups [][]transactions.SignedTxn
+ groupCtxs []*GroupContext
+ elementBacklogMessage []interface{}
+ messagesForTxn []int
+}
+
+func makeBatchLoad(l int) (bl batchLoad) {
+ bl.txnGroups = make([][]transactions.SignedTxn, 0, l)
+ bl.groupCtxs = make([]*GroupContext, 0, l)
+ bl.elementBacklogMessage = make([]interface{}, 0, l)
+ bl.messagesForTxn = make([]int, 0, l)
+ return bl
+}
+
+func (bl *batchLoad) addLoad(txngrp []transactions.SignedTxn, gctx *GroupContext, backlogMsg interface{}, numBatchableSigs int) {
+ bl.txnGroups = append(bl.txnGroups, txngrp)
+ bl.groupCtxs = append(bl.groupCtxs, gctx)
+ bl.elementBacklogMessage = append(bl.elementBacklogMessage, backlogMsg)
+ bl.messagesForTxn = append(bl.messagesForTxn, numBatchableSigs)
+
+}
+
+// LedgerForStreamVerifier defines the ledger methods used by the StreamVerifier.
+type LedgerForStreamVerifier interface {
+ logic.LedgerForSignature
+ RegisterBlockListeners([]ledgercore.BlockListener)
+ Latest() basics.Round
+ BlockHdr(rnd basics.Round) (blk bookkeeping.BlockHeader, err error)
+}
+
+// MakeStreamVerifier creates a new stream verifier and returns the chans used to send txn groups
+// to it and obtain the txn signature verification result from
+func MakeStreamVerifier(stxnChan <-chan *UnverifiedElement, resultChan chan<- *VerificationResult,
+ droppedChan chan<- *UnverifiedElement, ledger LedgerForStreamVerifier,
+ verificationPool execpool.BacklogPool, cache VerifiedTransactionCache) (*StreamVerifier, error) {
+
+ latest := ledger.Latest()
+ latestHdr, err := ledger.BlockHdr(latest)
+ if err != nil {
+ return nil, errors.New("MakeStreamVerifier: Could not get header for previous block")
+ }
+
+ nbw := MakeNewBlockWatcher(latestHdr)
+ ledger.RegisterBlockListeners([]ledgercore.BlockListener{nbw})
+
+ return &StreamVerifier{
+ resultChan: resultChan,
+ stxnChan: stxnChan,
+ droppedChan: droppedChan,
+ verificationPool: verificationPool,
+ cache: cache,
+ nbw: nbw,
+ ledger: ledger,
+ }, nil
+}
+
+// Start is called when the verifier is created and whenever it needs to restart after
+// the ctx is canceled
+func (sv *StreamVerifier) Start(ctx context.Context) {
+ sv.ctx = ctx
+ sv.activeLoopWg.Add(1)
+ go sv.batchingLoop()
+}
+
+// WaitForStop waits until the batching loop terminates afer the ctx is canceled
+func (sv *StreamVerifier) WaitForStop() {
+ sv.activeLoopWg.Wait()
+}
+
+func (sv *StreamVerifier) cleanup(pending []*UnverifiedElement) {
+ // report an error for the unchecked txns
+ // drop the messages without reporting if the receiver does not consume
+ for _, uel := range pending {
+ sv.sendResult(uel.TxnGroup, uel.BacklogMessage, errShuttingDownError)
+ }
+}
+
+func (sv *StreamVerifier) batchingLoop() {
+ defer sv.activeLoopWg.Done()
+ timer := time.NewTicker(waitForNextTxnDuration)
+ defer timer.Stop()
+ var added bool
+ var numberOfSigsInCurrent uint64
+ var numberOfBatchAttempts uint64
+ ue := make([]*UnverifiedElement, 0, 8)
+ defer func() { sv.cleanup(ue) }()
+ for {
+ select {
+ case stx := <-sv.stxnChan:
+ numberOfBatchableSigsInGroup, err := getNumberOfBatchableSigsInGroup(stx.TxnGroup)
+ if err != nil {
+ // wrong number of signatures
+ sv.sendResult(stx.TxnGroup, stx.BacklogMessage, err)
+ continue
+ }
+
+ // if no batchable signatures here, send this as a task of its own
+ if numberOfBatchableSigsInGroup == 0 {
+ err := sv.addVerificationTaskToThePoolNow([]*UnverifiedElement{stx})
+ if err != nil {
+ return
+ }
+ continue // stx is handled, continue
+ }
+
+ // add this txngrp to the list of batchable txn groups
+ numberOfSigsInCurrent = numberOfSigsInCurrent + numberOfBatchableSigsInGroup
+ ue = append(ue, stx)
+ if numberOfSigsInCurrent > txnPerWorksetThreshold {
+ // enough transaction in the batch to efficiently verify
+
+ if numberOfSigsInCurrent > batchSizeBlockLimit {
+ // do not consider adding more txns to this batch.
+ // bypass the exec pool situation and queue anyway
+ // this is to prevent creation of very large batches
+ err := sv.addVerificationTaskToThePoolNow(ue)
+ if err != nil {
+ return
+ }
+ added = true
+ } else {
+ added, err = sv.tryAddVerificationTaskToThePool(ue)
+ if err != nil {
+ return
+ }
+ }
+ if added {
+ numberOfSigsInCurrent = 0
+ ue = make([]*UnverifiedElement, 0, 8)
+ numberOfBatchAttempts = 0
+ } else {
+ // was not added because of the exec pool buffer length
+ numberOfBatchAttempts++
+ }
+ }
+ case <-timer.C:
+ // timer ticked. it is time to send the batch even if it is not full
+ if numberOfSigsInCurrent == 0 {
+ // nothing batched yet... wait some more
+ continue
+ }
+ var err error
+ if numberOfBatchAttempts > 1 {
+ // bypass the exec pool situation and queue anyway
+ // this is to prevent long delays in transaction propagation
+ // at least one transaction here has waited 3 x waitForNextTxnDuration
+ err = sv.addVerificationTaskToThePoolNow(ue)
+ added = true
+ } else {
+ added, err = sv.tryAddVerificationTaskToThePool(ue)
+ }
+ if err != nil {
+ return
+ }
+ if added {
+ numberOfSigsInCurrent = 0
+ ue = make([]*UnverifiedElement, 0, 8)
+ numberOfBatchAttempts = 0
+ } else {
+ // was not added because of the exec pool buffer length. wait for some more txns
+ numberOfBatchAttempts++
+ }
+ case <-sv.ctx.Done():
+ return
+ }
+ }
+}
+
+func (sv *StreamVerifier) sendResult(veTxnGroup []transactions.SignedTxn, veBacklogMessage interface{}, err error) {
+ // send the txn result out the pipe
+ select {
+ case sv.resultChan <- &VerificationResult{
+ TxnGroup: veTxnGroup,
+ BacklogMessage: veBacklogMessage,
+ Err: err,
+ }:
+ default:
+ // we failed to write to the output queue, since the queue was full.
+ sv.droppedChan <- &UnverifiedElement{veTxnGroup, veBacklogMessage}
+ }
+}
+
+func (sv *StreamVerifier) tryAddVerificationTaskToThePool(ue []*UnverifiedElement) (added bool, err error) {
+ // if the exec pool buffer is full, can go back and collect
+ // more signatures instead of waiting in the exec pool buffer
+ // more signatures to the batch do not harm performance but introduce latency when delayed (see crypto.BenchmarkBatchVerifierBig)
+
+ // if the buffer is full
+ if l, c := sv.verificationPool.BufferSize(); l == c {
+ return false, nil
+ }
+ err = sv.addVerificationTaskToThePoolNow(ue)
+ if err != nil {
+ // An error is returned when the context of the pool expires
+ return false, err
+ }
+ return true, nil
+}
+
+func (sv *StreamVerifier) addVerificationTaskToThePoolNow(ue []*UnverifiedElement) error {
+ // if the context is canceled when the task is in the queue, it should be canceled
+ // copy the ctx here so that when the StreamVerifier is started again, and a new context
+ // is created, this task still gets canceled due to the ctx at the time of this task
+ taskCtx := sv.ctx
+ function := func(arg interface{}) interface{} {
+ if taskCtx.Err() != nil {
+ // ctx is canceled. the results will be returned
+ sv.cleanup(ue)
+ return nil
+ }
+
+ ue := arg.([]*UnverifiedElement)
+ batchVerifier := crypto.MakeBatchVerifier()
+
+ bl := makeBatchLoad(len(ue))
+ // TODO: separate operations here, and get the sig verification inside the LogicSig to the batch here
+ blockHeader := sv.nbw.getBlockHeader()
+ for _, ue := range ue {
+ groupCtx, err := txnGroupBatchPrep(ue.TxnGroup, blockHeader, sv.ledger, batchVerifier, nil)
+ if err != nil {
+ // verification failed, no need to add the sig to the batch, report the error
+ sv.sendResult(ue.TxnGroup, ue.BacklogMessage, err)
+ continue
+ }
+ totalBatchCount := batchVerifier.GetNumberOfEnqueuedSignatures()
+ bl.addLoad(ue.TxnGroup, groupCtx, ue.BacklogMessage, totalBatchCount)
+ }
+
+ failed, err := batchVerifier.VerifyWithFeedback()
+ // this error can only be crypto.ErrBatchHasFailedSigs
+ if err == nil { // success, all signatures verified
+ for i := range bl.txnGroups {
+ sv.sendResult(bl.txnGroups[i], bl.elementBacklogMessage[i], nil)
+ }
+ sv.cache.AddPayset(bl.txnGroups, bl.groupCtxs)
+ return nil
+ }
+
+ verifiedTxnGroups := make([][]transactions.SignedTxn, 0, len(bl.txnGroups))
+ verifiedGroupCtxs := make([]*GroupContext, 0, len(bl.groupCtxs))
+ failedSigIdx := 0
+ for txgIdx := range bl.txnGroups {
+ txGroupSigFailed := false
+ for failedSigIdx < bl.messagesForTxn[txgIdx] {
+ if failed[failedSigIdx] {
+ // if there is a failed sig check, then no need to check the rest of the
+ // sigs for this txnGroup
+ failedSigIdx = bl.messagesForTxn[txgIdx]
+ txGroupSigFailed = true
+ } else {
+ // proceed to check the next sig belonging to this txnGroup
+ failedSigIdx++
+ }
+ }
+ var result error
+ if !txGroupSigFailed {
+ verifiedTxnGroups = append(verifiedTxnGroups, bl.txnGroups[txgIdx])
+ verifiedGroupCtxs = append(verifiedGroupCtxs, bl.groupCtxs[txgIdx])
+ } else {
+ result = err
+ }
+ sv.sendResult(bl.txnGroups[txgIdx], bl.elementBacklogMessage[txgIdx], result)
+ }
+ // loading them all at once by locking the cache once
+ sv.cache.AddPayset(verifiedTxnGroups, verifiedGroupCtxs)
+ return nil
+ }
+
+ // EnqueueBacklog returns an error when the context is canceled
+ err := sv.verificationPool.EnqueueBacklog(sv.ctx, function, ue, nil)
+ if err != nil {
+ logging.Base().Infof("addVerificationTaskToThePoolNow: EnqueueBacklog returned an error and StreamVerifier will stop: %v", err)
+ }
+ return err
+}
+
+func getNumberOfBatchableSigsInGroup(stxs []transactions.SignedTxn) (batchSigs uint64, err error) {
+ batchSigs = 0
+ for i := range stxs {
+ count, err := getNumberOfBatchableSigsInTxn(&stxs[i])
+ if err != nil {
+ return 0, err
+ }
+ batchSigs = batchSigs + count
+ }
+ return
+}
+
+func getNumberOfBatchableSigsInTxn(stx *transactions.SignedTxn) (uint64, error) {
+ sigType, err := checkTxnSigTypeCounts(stx)
+ if err != nil {
+ return 0, err
+ }
+ switch sigType {
+ case regularSig:
+ return 1, nil
+ case multiSig:
+ sig := stx.Msig
+ batchSigs := uint64(0)
+ for _, subsigi := range sig.Subsigs {
+ if (subsigi.Sig != crypto.Signature{}) {
+ batchSigs++
+ }
+ }
+ return batchSigs, nil
+ case logicSig:
+ // Currently the sigs in here are not batched. Something to consider later.
+ return 0, nil
+ case stateProofTxn:
+ return 0, nil
+ default:
+ // this case is impossible
+ return 0, nil
+ }
+}
diff --git a/data/transactions/verify/txn_test.go b/data/transactions/verify/txn_test.go
index 3e23c8f35..eb03917fb 100644
--- a/data/transactions/verify/txn_test.go
+++ b/data/transactions/verify/txn_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -17,8 +17,14 @@
package verify
import (
+ "bytes"
"context"
+ "encoding/binary"
+ "errors"
+ "fmt"
"math/rand"
+ "runtime"
+ "sync"
"testing"
"time"
@@ -30,14 +36,19 @@ import (
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
+ "github.com/algorand/go-algorand/data/transactions/logic/mocktracer"
+ "github.com/algorand/go-algorand/data/txntest"
+ "github.com/algorand/go-algorand/ledger/ledgercore"
+ "github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/test/partitiontest"
"github.com/algorand/go-algorand/util/execpool"
+ "github.com/algorand/go-algorand/util/metrics"
)
var feeSink = basics.Address{0x7, 0xda, 0xcb, 0x4b, 0x6d, 0x9e, 0xd1, 0x41, 0xb1, 0x75, 0x76, 0xbd, 0x45, 0x9a, 0xe6, 0x42, 0x1d, 0x48, 0x6d, 0xa3, 0xd4, 0xef, 0x22, 0x47, 0xc4, 0x9, 0xa3, 0x96, 0xb8, 0x2e, 0xa2, 0x21}
var poolAddr = basics.Address{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
-var blockHeader = bookkeeping.BlockHeader{
+var blockHeader = &bookkeeping.BlockHeader{
RewardsState: bookkeeping.RewardsState{
FeeSink: feeSink,
RewardsPool: poolAddr,
@@ -46,6 +57,8 @@ var blockHeader = bookkeeping.BlockHeader{
CurrentProtocol: protocol.ConsensusCurrentVersion,
},
}
+var protoMaxGroupSize = config.Consensus[protocol.ConsensusCurrentVersion].MaxTxGroupSize
+var txBacklogSize = config.Consensus[protocol.ConsensusCurrentVersion].MaxTxnBytesPerBlock / 200
var spec = transactions.SpecialAddresses{
FeeSink: feeSink,
@@ -55,27 +68,29 @@ var spec = transactions.SpecialAddresses{
func verifyTxn(s *transactions.SignedTxn, txnIdx int, groupCtx *GroupContext) error {
batchVerifier := crypto.MakeBatchVerifier()
- if err := txnBatchPrep(s, txnIdx, groupCtx, batchVerifier); err != nil {
+ if err := txnBatchPrep(s, txnIdx, groupCtx, batchVerifier, nil); err != nil {
return err
}
return batchVerifier.Verify()
}
type DummyLedgerForSignature struct {
+ badHdr bool
}
func (d *DummyLedgerForSignature) BlockHdrCached(basics.Round) (bookkeeping.BlockHeader, error) {
- return bookkeeping.BlockHeader{
- Round: 50,
- GenesisHash: crypto.Hash([]byte{1, 2, 3, 4, 5}),
- UpgradeState: bookkeeping.UpgradeState{
- CurrentProtocol: protocol.ConsensusCurrentVersion,
- },
- RewardsState: bookkeeping.RewardsState{
- FeeSink: feeSink,
- RewardsPool: poolAddr,
- },
- }, nil
+ return createDummyBlockHeader(), nil
+}
+func (d *DummyLedgerForSignature) BlockHdr(rnd basics.Round) (blk bookkeeping.BlockHeader, err error) {
+ if d.badHdr {
+ return bookkeeping.BlockHeader{}, fmt.Errorf("test error block hdr")
+ }
+ return createDummyBlockHeader(), nil
+}
+func (d *DummyLedgerForSignature) Latest() basics.Round {
+ return 0
+}
+func (d *DummyLedgerForSignature) RegisterBlockListeners([]ledgercore.BlockListener) {
}
func keypair() *crypto.SignatureSecrets {
@@ -108,20 +123,7 @@ func generateMultiSigTxn(numTxs, numAccs int, blockRound basics.Round, t *testin
exp = int(blockRound) + rand.Intn(30)
}
- txs[i] = transactions.Transaction{
- Type: protocol.PaymentTx,
- Header: transactions.Header{
- Sender: multiAddress[s],
- Fee: basics.MicroAlgos{Raw: f},
- FirstValid: basics.Round(iss),
- LastValid: basics.Round(exp),
- GenesisHash: crypto.Hash([]byte{1, 2, 3, 4, 5}),
- },
- PaymentTxnFields: transactions.PaymentTxnFields{
- Receiver: multiAddress[r],
- Amount: basics.MicroAlgos{Raw: uint64(a)},
- },
- }
+ txs[i] = createPayTransaction(f, iss, exp, a, multiAddress[s], multiAddress[r])
signed[i].Txn = txs[i]
// create multi sig that 2 out of 3 has signed the txn
@@ -172,7 +174,7 @@ func generateAccounts(numAccs int) ([]*crypto.SignatureSecrets, []basics.Address
return secrets, addresses, pks
}
-func generateTestObjects(numTxs, numAccs int, blockRound basics.Round) ([]transactions.Transaction, []transactions.SignedTxn, []*crypto.SignatureSecrets, []basics.Address) {
+func generateTestObjects(numTxs, numAccs, noteOffset int, blockRound basics.Round) ([]transactions.Transaction, []transactions.SignedTxn, []*crypto.SignatureSecrets, []basics.Address) {
txs := make([]transactions.Transaction, numTxs)
signed := make([]transactions.SignedTxn, numTxs)
secrets, addresses, _ := generateAccounts(numAccs)
@@ -192,20 +194,11 @@ func generateTestObjects(numTxs, numAccs int, blockRound basics.Round) ([]transa
exp = int(blockRound) + rand.Intn(30)
}
- txs[i] = transactions.Transaction{
- Type: protocol.PaymentTx,
- Header: transactions.Header{
- Sender: addresses[s],
- Fee: basics.MicroAlgos{Raw: f},
- FirstValid: basics.Round(iss),
- LastValid: basics.Round(exp),
- GenesisHash: crypto.Hash([]byte{1, 2, 3, 4, 5}),
- },
- PaymentTxnFields: transactions.PaymentTxnFields{
- Receiver: addresses[r],
- Amount: basics.MicroAlgos{Raw: uint64(a)},
- },
- }
+ txs[i] = createPayTransaction(f, iss, exp, a, addresses[s], addresses[r])
+ noteField := make([]byte, binary.MaxVarintLen64)
+ binary.PutUvarint(noteField, uint64(i+noteOffset))
+ txs[i].Note = noteField
+
signed[i] = txs[i].Sign(secrets[s])
u += 100
}
@@ -218,7 +211,7 @@ func TestSignedPayment(t *testing.T) {
proto := config.Consensus[protocol.ConsensusCurrentVersion]
- payments, stxns, secrets, addrs := generateTestObjects(1, 1, 0)
+ payments, stxns, secrets, addrs := generateTestObjects(1, 1, 0, 0)
payment, stxn, secret, addr := payments[0], stxns[0], secrets[0], addrs[0]
groupCtx, err := PrepareGroupContext(stxns, blockHeader, nil)
@@ -239,7 +232,7 @@ func TestSignedPayment(t *testing.T) {
func TestTxnValidationEncodeDecode(t *testing.T) {
partitiontest.PartitionTest(t)
- _, signed, _, _ := generateTestObjects(100, 50, 0)
+ _, signed, _, _ := generateTestObjects(100, 50, 0, 0)
for _, txn := range signed {
groupCtx, err := PrepareGroupContext([]transactions.SignedTxn{txn}, blockHeader, nil)
@@ -261,7 +254,7 @@ func TestTxnValidationEncodeDecode(t *testing.T) {
func TestTxnValidationEmptySig(t *testing.T) {
partitiontest.PartitionTest(t)
- _, signed, _, _ := generateTestObjects(100, 50, 0)
+ _, signed, _, _ := generateTestObjects(100, 50, 0, 0)
for _, txn := range signed {
groupCtx, err := PrepareGroupContext([]transactions.SignedTxn{txn}, blockHeader, nil)
@@ -281,7 +274,7 @@ func TestTxnValidationEmptySig(t *testing.T) {
const spProto = protocol.ConsensusVersion("test-state-proof-enabled")
-func TestTxnValidationStateProof(t *testing.T) {
+func TestTxnValidationStateProof(t *testing.T) { //nolint:paralleltest // Not parallel because it modifies config.Consensus
partitiontest.PartitionTest(t)
proto := config.Consensus[protocol.ConsensusCurrentVersion]
@@ -299,7 +292,7 @@ func TestTxnValidationStateProof(t *testing.T) {
},
}
- var blockHeader = bookkeeping.BlockHeader{
+ var blockHeader = &bookkeeping.BlockHeader{
RewardsState: bookkeeping.RewardsState{
FeeSink: feeSink,
RewardsPool: poolAddr,
@@ -369,17 +362,105 @@ func TestDecodeNil(t *testing.T) {
}
}
+func TestTxnGroupWithTracer(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ proto := config.Consensus[protocol.ConsensusCurrentVersion]
+
+ account := keypair()
+ accountAddr := basics.Address(account.SignatureVerifier)
+
+ ops1, err := logic.AssembleString(`#pragma version 6
+pushint 1`)
+ require.NoError(t, err)
+ program1 := ops1.Program
+ program1Addr := basics.Address(logic.HashProgram(program1))
+
+ ops2, err := logic.AssembleString(`#pragma version 6
+pushbytes "test"
+pop
+pushint 1`)
+ require.NoError(t, err)
+ program2 := ops2.Program
+ program2Addr := basics.Address(logic.HashProgram(program2))
+
+ // this shouldn't be invoked during this test
+ appProgram := "err"
+
+ lsigPay := txntest.Txn{
+ Type: protocol.PaymentTx,
+ Sender: program1Addr,
+ Receiver: accountAddr,
+ Fee: proto.MinTxnFee,
+ }
+
+ normalSigAppCall := txntest.Txn{
+ Type: protocol.ApplicationCallTx,
+ Sender: accountAddr,
+ ApprovalProgram: appProgram,
+ ClearStateProgram: appProgram,
+ Fee: proto.MinTxnFee,
+ }
+
+ lsigAppCall := txntest.Txn{
+ Type: protocol.ApplicationCallTx,
+ Sender: program2Addr,
+ ApprovalProgram: appProgram,
+ ClearStateProgram: appProgram,
+ Fee: proto.MinTxnFee,
+ }
+
+ txntest.Group(&lsigPay, &normalSigAppCall, &lsigAppCall)
+
+ txgroup := []transactions.SignedTxn{
+ {
+ Lsig: transactions.LogicSig{
+ Logic: program1,
+ },
+ Txn: lsigPay.Txn(),
+ },
+ normalSigAppCall.Txn().Sign(account),
+ {
+ Lsig: transactions.LogicSig{
+ Logic: program2,
+ },
+ Txn: lsigAppCall.Txn(),
+ },
+ }
+
+ mockTracer := &mocktracer.Tracer{}
+ _, err = TxnGroupWithTracer(txgroup, blockHeader, nil, logic.NoHeaderLedger{}, mockTracer)
+ require.NoError(t, err)
+
+ expectedEvents := []mocktracer.Event{
+ mocktracer.BeforeProgram(logic.ModeSig), // first txn start
+ mocktracer.BeforeOpcode(), mocktracer.AfterOpcode(), // first txn LogicSig: 1 op
+ mocktracer.AfterProgram(logic.ModeSig), // first txn end
+ // nothing for second txn (not signed with a LogicSig)
+ mocktracer.BeforeProgram(logic.ModeSig), // third txn start
+ mocktracer.BeforeOpcode(), mocktracer.AfterOpcode(), mocktracer.BeforeOpcode(), mocktracer.AfterOpcode(), mocktracer.BeforeOpcode(), mocktracer.AfterOpcode(), // third txn LogicSig: 3 ops
+ mocktracer.AfterProgram(logic.ModeSig), // third txn end
+ }
+ require.Equal(t, expectedEvents, mockTracer.Events)
+}
+
func TestPaysetGroups(t *testing.T) {
partitiontest.PartitionTest(t)
- _, signedTxn, secrets, addrs := generateTestObjects(10000, 20, 50)
+ if testing.Short() {
+ t.Log("this is a long test and skipping for -short")
+ return
+ }
+
+ _, signedTxn, secrets, addrs := generateTestObjects(10000, 20, 0, 50)
blkHdr := createDummyBlockHeader()
execPool := execpool.MakePool(t)
verificationPool := execpool.MakeBacklog(execPool, 64, execpool.LowPriority, t)
defer verificationPool.Shutdown()
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
startPaysetGroupsTime := time.Now()
err := PaysetGroups(context.Background(), txnGroups, blkHdr, verificationPool, MakeVerifiedTransactionCache(50000), nil)
@@ -399,9 +480,9 @@ func TestPaysetGroups(t *testing.T) {
// we define a test that would take 10 seconds to execute, and try to abort at 1.5 seconds.
txnCount := len(signedTxn) * 10 * int(time.Second/paysetGroupDuration)
- _, signedTxn, secrets, addrs = generateTestObjects(txnCount, 20, 50)
+ _, signedTxn, secrets, addrs = generateTestObjects(txnCount, 20, 0, 50)
- txnGroups = generateTransactionGroups(signedTxn, secrets, addrs)
+ txnGroups = generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
ctx, ctxCancelFunc := context.WithTimeout(context.Background(), 1500*time.Millisecond)
defer ctxCancelFunc()
@@ -442,14 +523,14 @@ func BenchmarkPaysetGroups(b *testing.B) {
if b.N < 2000 {
b.N = 2000
}
- _, signedTxn, secrets, addrs := generateTestObjects(b.N, 20, 50)
+ _, signedTxn, secrets, addrs := generateTestObjects(b.N, 20, 0, 50)
blkHdr := createDummyBlockHeader()
execPool := execpool.MakePool(b)
verificationPool := execpool.MakeBacklog(execPool, 64, execpool.LowPriority, b)
defer verificationPool.Shutdown()
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
cache := MakeVerifiedTransactionCache(50000)
b.ResetTimer()
@@ -461,7 +542,7 @@ func BenchmarkPaysetGroups(b *testing.B) {
func TestTxnGroupMixedSignatures(t *testing.T) {
partitiontest.PartitionTest(t)
- _, signedTxn, secrets, addrs := generateTestObjects(1, 20, 50)
+ _, signedTxn, secrets, addrs := generateTestObjects(1, 20, 0, 50)
blkHdr := createDummyBlockHeader()
// add a simple logic that verifies this condition:
@@ -472,16 +553,16 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
==`)
require.NoError(t, err)
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
dummyLedger := DummyLedgerForSignature{}
- _, err = TxnGroup(txnGroups[0], blkHdr, nil, &dummyLedger)
+ _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger)
require.NoError(t, err)
///// no sig
tmpSig := txnGroups[0][0].Sig
txnGroups[0][0].Sig = crypto.Signature{}
- _, err = TxnGroup(txnGroups[0], blkHdr, nil, &dummyLedger)
+ _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger)
require.Error(t, err)
require.Contains(t, err.Error(), "has no sig")
txnGroups[0][0].Sig = tmpSig
@@ -492,14 +573,14 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
Key: crypto.PublicKey{0x1},
Sig: crypto.Signature{0x2},
}
- _, err = TxnGroup(txnGroups[0], blkHdr, nil, &dummyLedger)
+ _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger)
require.Error(t, err)
require.Contains(t, err.Error(), "should only have one of Sig or Msig or LogicSig")
txnGroups[0][0].Msig.Subsigs = nil
///// Sig + logic
txnGroups[0][0].Lsig.Logic = op.Program
- _, err = TxnGroup(txnGroups[0], blkHdr, nil, &dummyLedger)
+ _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger)
require.Error(t, err)
require.Contains(t, err.Error(), "should only have one of Sig or Msig or LogicSig")
txnGroups[0][0].Lsig.Logic = []byte{}
@@ -512,7 +593,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
Key: crypto.PublicKey{0x1},
Sig: crypto.Signature{0x2},
}
- _, err = TxnGroup(txnGroups[0], blkHdr, nil, &dummyLedger)
+ _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger)
require.Error(t, err)
require.Contains(t, err.Error(), "should only have one of Sig or Msig or LogicSig")
txnGroups[0][0].Lsig.Logic = []byte{}
@@ -528,36 +609,45 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
Key: crypto.PublicKey{0x1},
Sig: crypto.Signature{0x2},
}
- _, err = TxnGroup(txnGroups[0], blkHdr, nil, &dummyLedger)
+ _, err = TxnGroup(txnGroups[0], &blkHdr, nil, &dummyLedger)
require.Error(t, err)
require.Contains(t, err.Error(), "should only have one of Sig or Msig")
}
-func generateTransactionGroups(signedTxns []transactions.SignedTxn, secrets []*crypto.SignatureSecrets, addrs []basics.Address) [][]transactions.SignedTxn {
+func generateTransactionGroups(maxGroupSize int, signedTxns []transactions.SignedTxn,
+ secrets []*crypto.SignatureSecrets, addrs []basics.Address) [][]transactions.SignedTxn {
addrToSecret := make(map[basics.Address]*crypto.SignatureSecrets)
for i, addr := range addrs {
addrToSecret[addr] = secrets[i]
}
txnGroups := make([][]transactions.SignedTxn, 0, len(signedTxns))
- for i := 0; i < len(signedTxns); i++ {
- txnPerGroup := 1 + rand.Intn(15)
- if i+txnPerGroup >= len(signedTxns) {
- txnPerGroup = len(signedTxns) - i - 1
+ for i := 0; i < len(signedTxns); {
+ txnsInGroup := rand.Intn(protoMaxGroupSize-1) + 1
+ if txnsInGroup > maxGroupSize {
+ txnsInGroup = maxGroupSize
+ }
+ if i+txnsInGroup > len(signedTxns) {
+ txnsInGroup = len(signedTxns) - i
}
- newGroup := signedTxns[i : i+txnPerGroup+1]
+
+ newGroup := signedTxns[i : i+txnsInGroup]
var txGroup transactions.TxGroup
- for _, txn := range newGroup {
- txGroup.TxGroupHashes = append(txGroup.TxGroupHashes, crypto.HashObj(txn.Txn))
+ if txnsInGroup > 1 {
+ for _, txn := range newGroup {
+ txGroup.TxGroupHashes = append(txGroup.TxGroupHashes, crypto.HashObj(txn.Txn))
+ }
}
groupHash := crypto.HashObj(txGroup)
for j := range newGroup {
- newGroup[j].Txn.Group = groupHash
+ if txnsInGroup > 1 {
+ newGroup[j].Txn.Group = groupHash
+ }
newGroup[j].Sig = addrToSecret[newGroup[j].Txn.Sender].Sign(&newGroup[j].Txn)
}
txnGroups = append(txnGroups, newGroup)
- i += txnPerGroup
+ i += txnsInGroup
}
return txnGroups
@@ -566,17 +656,17 @@ func generateTransactionGroups(signedTxns []transactions.SignedTxn, secrets []*c
func TestTxnGroupCacheUpdate(t *testing.T) {
partitiontest.PartitionTest(t)
- _, signedTxn, secrets, addrs := generateTestObjects(100, 20, 50)
+ _, signedTxn, secrets, addrs := generateTestObjects(100, 20, 0, 50)
blkHdr := createDummyBlockHeader()
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
breakSignatureFunc := func(txn *transactions.SignedTxn) {
txn.Sig[0]++
}
restoreSignatureFunc := func(txn *transactions.SignedTxn) {
txn.Sig[0]--
}
- verifyGroup(t, txnGroups, blkHdr, breakSignatureFunc, restoreSignatureFunc, crypto.ErrBatchVerificationFailed.Error())
+ verifyGroup(t, txnGroups, &blkHdr, breakSignatureFunc, restoreSignatureFunc, crypto.ErrBatchHasFailedSigs.Error())
}
// TestTxnGroupCacheUpdateMultiSig makes sure that a payment transaction signed with multisig
@@ -598,7 +688,7 @@ func TestTxnGroupCacheUpdateMultiSig(t *testing.T) {
restoreSignatureFunc := func(txn *transactions.SignedTxn) {
txn.Msig.Subsigs[0].Sig[0]--
}
- verifyGroup(t, txnGroups, blkHdr, breakSignatureFunc, restoreSignatureFunc, crypto.ErrBatchVerificationFailed.Error())
+ verifyGroup(t, txnGroups, &blkHdr, breakSignatureFunc, restoreSignatureFunc, crypto.ErrBatchHasFailedSigs.Error())
}
// TestTxnGroupCacheUpdateFailLogic test makes sure that a payment transaction contains a logic (and no signature)
@@ -606,7 +696,7 @@ func TestTxnGroupCacheUpdateMultiSig(t *testing.T) {
func TestTxnGroupCacheUpdateFailLogic(t *testing.T) {
partitiontest.PartitionTest(t)
- _, signedTxn, _, _ := generateTestObjects(100, 20, 50)
+ _, signedTxn, _, _ := generateTestObjects(100, 20, 0, 50)
blkHdr := createDummyBlockHeader()
// sign the transaction with logic
@@ -638,7 +728,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
txn.Lsig.Args[0][0]--
}
initCounter := logicCostTotal.GetUint64Value()
- verifyGroup(t, txnGroups, blkHdr, breakSignatureFunc, restoreSignatureFunc, "rejected by logic")
+ verifyGroup(t, txnGroups, &blkHdr, breakSignatureFunc, restoreSignatureFunc, "rejected by logic")
currentCounter := logicCostTotal.GetUint64Value()
require.Greater(t, currentCounter, initCounter)
}
@@ -649,7 +739,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
func TestTxnGroupCacheUpdateLogicWithSig(t *testing.T) {
partitiontest.PartitionTest(t)
- _, signedTxn, secrets, addresses := generateTestObjects(100, 20, 50)
+ _, signedTxn, secrets, addresses := generateTestObjects(100, 20, 0, 50)
blkHdr := createDummyBlockHeader()
for i := 0; i < len(signedTxn); i++ {
@@ -683,7 +773,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
restoreSignatureFunc := func(txn *transactions.SignedTxn) {
txn.Lsig.Sig[0]--
}
- verifyGroup(t, txnGroups, blkHdr, breakSignatureFunc, restoreSignatureFunc, crypto.ErrBatchVerificationFailed.Error())
+ verifyGroup(t, txnGroups, &blkHdr, breakSignatureFunc, restoreSignatureFunc, crypto.ErrBatchHasFailedSigs.Error())
// signature is correct and logic fails
breakSignatureFunc = func(txn *transactions.SignedTxn) {
@@ -692,7 +782,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
restoreSignatureFunc = func(txn *transactions.SignedTxn) {
txn.Lsig.Args[0][0]--
}
- verifyGroup(t, txnGroups, blkHdr, breakSignatureFunc, restoreSignatureFunc, "rejected by logic")
+ verifyGroup(t, txnGroups, &blkHdr, breakSignatureFunc, restoreSignatureFunc, "rejected by logic")
}
// TestTxnGroupCacheUpdateLogicWithMultiSig makes sure that a payment transaction contains logicsig signed with multisig is valid
@@ -714,20 +804,7 @@ func TestTxnGroupCacheUpdateLogicWithMultiSig(t *testing.T) {
a := rand.Intn(1000)
f := config.Consensus[protocol.ConsensusCurrentVersion].MinTxnFee + uint64(rand.Intn(10))
- signedTxn[i].Txn = transactions.Transaction{
- Type: protocol.PaymentTx,
- Header: transactions.Header{
- Sender: multiAddress[s],
- Fee: basics.MicroAlgos{Raw: f},
- FirstValid: basics.Round(1),
- LastValid: basics.Round(100),
- GenesisHash: crypto.Hash([]byte{1, 2, 3, 4, 5}),
- },
- PaymentTxnFields: transactions.PaymentTxnFields{
- Receiver: multiAddress[r],
- Amount: basics.MicroAlgos{Raw: uint64(a)},
- },
- }
+ signedTxn[i].Txn = createPayTransaction(f, 1, 100, a, multiAddress[s], multiAddress[r])
// add a simple logic that verifies this condition:
// sha256(arg0) == base64decode(5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=)
op, err := logic.AssembleString(`arg 0
@@ -767,7 +844,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
txn.Lsig.Msig.Subsigs[0].Sig[0]--
}
- verifyGroup(t, txnGroups, blkHdr, breakSignatureFunc, restoreSignatureFunc, crypto.ErrBatchVerificationFailed.Error())
+ verifyGroup(t, txnGroups, &blkHdr, breakSignatureFunc, restoreSignatureFunc, crypto.ErrBatchHasFailedSigs.Error())
// signature is correct and logic fails
breakSignatureFunc = func(txn *transactions.SignedTxn) {
txn.Lsig.Args[0][0]++
@@ -775,7 +852,7 @@ byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
restoreSignatureFunc = func(txn *transactions.SignedTxn) {
txn.Lsig.Args[0][0]--
}
- verifyGroup(t, txnGroups, blkHdr, breakSignatureFunc, restoreSignatureFunc, "rejected by logic")
+ verifyGroup(t, txnGroups, &blkHdr, breakSignatureFunc, restoreSignatureFunc, "rejected by logic")
}
func createDummyBlockHeader() bookkeeping.BlockHeader {
@@ -792,10 +869,27 @@ func createDummyBlockHeader() bookkeeping.BlockHeader {
}
}
+func createPayTransaction(fee uint64, fv, lv, amount int, sender, receiver basics.Address) transactions.Transaction {
+ return transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: transactions.Header{
+ Sender: sender,
+ Fee: basics.MicroAlgos{Raw: fee},
+ FirstValid: basics.Round(fv),
+ LastValid: basics.Round(lv),
+ GenesisHash: crypto.Hash([]byte{1, 2, 3, 4, 5}),
+ },
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: receiver,
+ Amount: basics.MicroAlgos{Raw: uint64(amount)},
+ },
+ }
+}
+
// verifyGroup uses TxnGroup to verify txns and add them to the
// cache. Then makes sure that only the valid txns are verified and added to
// the cache.
-func verifyGroup(t *testing.T, txnGroups [][]transactions.SignedTxn, blkHdr bookkeeping.BlockHeader, breakSig func(txn *transactions.SignedTxn), restoreSig func(txn *transactions.SignedTxn), errorString string) {
+func verifyGroup(t *testing.T, txnGroups [][]transactions.SignedTxn, blkHdr *bookkeeping.BlockHeader, breakSig func(txn *transactions.SignedTxn), restoreSig func(txn *transactions.SignedTxn), errorString string) {
cache := MakeVerifiedTransactionCache(1000)
breakSig(&txnGroups[0][0])
@@ -859,25 +953,13 @@ func BenchmarkTxn(b *testing.B) {
if b.N < 2000 {
b.N = 2000
}
- _, signedTxn, secrets, addrs := generateTestObjects(b.N, 20, 50)
- blk := bookkeeping.Block{
- BlockHeader: bookkeeping.BlockHeader{
- Round: 50,
- GenesisHash: crypto.Hash([]byte{1, 2, 3, 4, 5}),
- UpgradeState: bookkeeping.UpgradeState{
- CurrentProtocol: protocol.ConsensusCurrentVersion,
- },
- RewardsState: bookkeeping.RewardsState{
- FeeSink: feeSink,
- RewardsPool: poolAddr,
- },
- },
- }
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ _, signedTxn, secrets, addrs := generateTestObjects(b.N, 20, 0, 50)
+ blk := bookkeeping.Block{BlockHeader: createDummyBlockHeader()}
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
b.ResetTimer()
for _, txnGroup := range txnGroups {
- groupCtx, err := PrepareGroupContext(txnGroup, blk.BlockHeader, nil)
+ groupCtx, err := PrepareGroupContext(txnGroup, &blk.BlockHeader, nil)
require.NoError(b, err)
for i, txn := range txnGroup {
err := verifyTxn(&txn, i, groupCtx)
@@ -886,3 +968,801 @@ func BenchmarkTxn(b *testing.B) {
}
b.StopTimer()
}
+
+var droppedFromPool = metrics.MakeCounter(metrics.MetricName{Name: "test_streamVerifierTestCore_messages_dropped_pool", Description: "Test streamVerifierTestCore messages dropped from pool"})
+
+func streamVerifierTestCore(txnGroups [][]transactions.SignedTxn, badTxnGroups map[uint64]struct{},
+ expectedError error, t *testing.T) (sv *StreamVerifier) {
+
+ numOfTxnGroups := len(txnGroups)
+ verificationPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, t)
+ defer verificationPool.Shutdown()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ cache := MakeVerifiedTransactionCache(50000)
+
+ defer cancel()
+
+ stxnChan := make(chan *UnverifiedElement)
+ resultChan := make(chan *VerificationResult, txBacklogSize)
+ droppedChan := make(chan *UnverifiedElement)
+ sv, err := MakeStreamVerifier(stxnChan, resultChan, droppedChan, &DummyLedgerForSignature{}, verificationPool, cache)
+ require.NoError(t, err)
+ sv.Start(ctx)
+
+ wg := sync.WaitGroup{}
+
+ errChan := make(chan error)
+ var badSigResultCounter int
+ var goodSigResultCounter int
+
+ wg.Add(1)
+ go processResults(ctx, errChan, resultChan, numOfTxnGroups, badTxnGroups, &badSigResultCounter, &goodSigResultCounter, &wg)
+
+ wg.Add(1)
+ // send txn groups to be verified
+ go func() {
+ defer wg.Done()
+ for _, tg := range txnGroups {
+ stxnChan <- &UnverifiedElement{TxnGroup: tg, BacklogMessage: nil}
+ }
+ }()
+
+ for err := range errChan {
+ require.ErrorContains(t, err, expectedError.Error())
+ }
+
+ wg.Wait()
+
+ verifyResults(txnGroups, badTxnGroups, cache, badSigResultCounter, goodSigResultCounter, t)
+ return sv
+}
+
+func processResults(ctx context.Context, errChan chan<- error, resultChan <-chan *VerificationResult,
+ numOfTxnGroups int, badTxnGroups map[uint64]struct{},
+ badSigResultCounter, goodSigResultCounter *int, wg *sync.WaitGroup) {
+ defer wg.Done()
+ defer close(errChan)
+ // process the results
+ for x := 0; x < numOfTxnGroups; x++ {
+ select {
+ case <-ctx.Done():
+ case result := <-resultChan:
+ u, _ := binary.Uvarint(result.TxnGroup[0].Txn.Note)
+ if _, has := badTxnGroups[u]; has {
+ (*badSigResultCounter)++
+ if result.Err == nil {
+ err := fmt.Errorf("%dth (%d)transaction varified with a bad sig", x, u)
+ errChan <- err
+ return
+ }
+ // we expected an error, but it is not the general crypto error
+ if result.Err != crypto.ErrBatchHasFailedSigs {
+ errChan <- result.Err
+ }
+ } else {
+ (*goodSigResultCounter)++
+ if result.Err != nil {
+ errChan <- result.Err
+ }
+ }
+ }
+ }
+}
+
+func verifyResults(txnGroups [][]transactions.SignedTxn, badTxnGroups map[uint64]struct{},
+ cache VerifiedTransactionCache,
+ badSigResultCounter, goodSigResultCounter int, t *testing.T) {
+ // check if all txns have been checked.
+ require.Equal(t, len(txnGroups), badSigResultCounter+goodSigResultCounter)
+ require.Equal(t, len(badTxnGroups), badSigResultCounter)
+
+ // check the cached transactions
+ // note that the result of each verified txn group is send before the batch is added to the cache
+ // the test does not know if the batch is not added to the cache yet, so some elts might be missing from the cache
+ unverifiedGroups := cache.GetUnverifiedTransactionGroups(txnGroups, spec, protocol.ConsensusCurrentVersion)
+ require.GreaterOrEqual(t, len(unverifiedGroups), badSigResultCounter)
+ for _, txn := range unverifiedGroups {
+ u, _ := binary.Uvarint(txn[0].Txn.Note)
+ if _, has := badTxnGroups[u]; has {
+ delete(badTxnGroups, u)
+ }
+ }
+ require.Empty(t, badTxnGroups, "unverifiedGroups should have all the transactions with invalid sigs")
+}
+
+func getSignedTransactions(numOfTxns, maxGrpSize, noteOffset int, badTxnProb float32) (txnGroups [][]transactions.SignedTxn, badTxnGroups map[uint64]struct{}) {
+
+ _, signedTxn, secrets, addrs := generateTestObjects(numOfTxns, 20, noteOffset, 50)
+ txnGroups = generateTransactionGroups(maxGrpSize, signedTxn, secrets, addrs)
+
+ badTxnGroups = make(map[uint64]struct{})
+
+ for tgi := range txnGroups {
+ if rand.Float32() < badTxnProb {
+ // make a bad sig
+ t := rand.Intn(len(txnGroups[tgi]))
+ txnGroups[tgi][t].Sig[0] = txnGroups[tgi][t].Sig[0] + 1
+ u, _ := binary.Uvarint(txnGroups[tgi][0].Txn.Note)
+ badTxnGroups[u] = struct{}{}
+ }
+ }
+ return
+
+}
+
+// TestStreamVerifier tests the basic functionality
+func TestStreamVerifier(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ numOfTxns := 4000
+ txnGroups, badTxnGroups := getSignedTransactions(numOfTxns, protoMaxGroupSize, 0, 0.5)
+
+ sv := streamVerifierTestCore(txnGroups, badTxnGroups, nil, t)
+ sv.WaitForStop()
+}
+
+// TestStreamVerifierCases tests various valid and invalid transaction signature cases
+func TestStreamVerifierCases(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ numOfTxns := 10
+ txnGroups, badTxnGroups := getSignedTransactions(numOfTxns, 1, 0, 0)
+ mod := 1
+
+ // txn with 0 sigs
+ txnGroups[mod][0].Sig = crypto.Signature{}
+ u, _ := binary.Uvarint(txnGroups[mod][0].Txn.Note)
+ badTxnGroups[u] = struct{}{}
+ sv := streamVerifierTestCore(txnGroups, badTxnGroups, errTxnSigHasNoSig, t)
+ sv.WaitForStop()
+ mod++
+
+ _, signedTxns, secrets, addrs := generateTestObjects(numOfTxns, 20, 0, 50)
+ txnGroups = generateTransactionGroups(1, signedTxns, secrets, addrs)
+ badTxnGroups = make(map[uint64]struct{})
+
+ // invalid stateproof txn
+ txnGroups[mod][0].Sig = crypto.Signature{}
+ txnGroups[mod][0].Txn.Type = protocol.StateProofTx
+ txnGroups[mod][0].Txn.Header.Sender = transactions.StateProofSender
+ u, _ = binary.Uvarint(txnGroups[mod][0].Txn.Note)
+ badTxnGroups[u] = struct{}{}
+ errFeeMustBeZeroInStateproofTxn := errors.New("fee must be zero in state-proof transaction")
+ sv = streamVerifierTestCore(txnGroups, badTxnGroups, errFeeMustBeZeroInStateproofTxn, t)
+ sv.WaitForStop()
+ mod++
+
+ _, signedTxns, secrets, addrs = generateTestObjects(numOfTxns, 20, 0, 50)
+ txnGroups = generateTransactionGroups(1, signedTxns, secrets, addrs)
+ badTxnGroups = make(map[uint64]struct{})
+
+ // acceptable stateproof txn
+ txnGroups[mod][0].Sig = crypto.Signature{}
+ txnGroups[mod][0].Txn.Note = nil
+ txnGroups[mod][0].Txn.Type = protocol.StateProofTx
+ txnGroups[mod][0].Txn.Header.Fee = basics.MicroAlgos{Raw: 0}
+ txnGroups[mod][0].Txn.Header.Sender = transactions.StateProofSender
+ txnGroups[mod][0].Txn.PaymentTxnFields = transactions.PaymentTxnFields{}
+ sv = streamVerifierTestCore(txnGroups, badTxnGroups, nil, t)
+ sv.WaitForStop()
+ mod++
+
+ // multisig
+ _, mSigTxn, _, _ := generateMultiSigTxn(1, 6, 50, t)
+ txnGroups[mod] = mSigTxn
+ sv = streamVerifierTestCore(txnGroups, badTxnGroups, nil, t)
+ sv.WaitForStop()
+ mod++
+
+ _, signedTxn, secrets, addrs := generateTestObjects(numOfTxns, 20, 0, 50)
+ txnGroups = generateTransactionGroups(1, signedTxn, secrets, addrs)
+ badTxnGroups = make(map[uint64]struct{})
+
+ // logicsig
+ // add a simple logic that verifies this condition:
+ // sha256(arg0) == base64decode(5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=)
+ op, err := logic.AssembleString(`arg 0
+sha256
+byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
+==`)
+ require.NoError(t, err)
+ s := rand.Intn(len(secrets))
+ txnGroups[mod][0].Sig = crypto.Signature{}
+ txnGroups[mod][0].Txn.Sender = addrs[s]
+ txnGroups[mod][0].Lsig.Args = [][]byte{[]byte("=0\x97S\x85H\xe9\x91B\xfd\xdb;1\xf5Z\xaec?\xae\xf2I\x93\x08\x12\x94\xaa~\x06\x08\x849b")}
+ txnGroups[mod][0].Lsig.Logic = op.Program
+ program := logic.Program(op.Program)
+ txnGroups[mod][0].Lsig.Sig = secrets[s].Sign(program)
+ sv = streamVerifierTestCore(txnGroups, badTxnGroups, nil, t)
+ sv.WaitForStop()
+ mod++
+
+ // bad lgicsig
+ s = rand.Intn(len(secrets))
+ txnGroups[mod][0].Sig = crypto.Signature{}
+ txnGroups[mod][0].Txn.Sender = addrs[s]
+ txnGroups[mod][0].Lsig.Args = [][]byte{[]byte("=0\x97S\x85H\xe9\x91B\xfd\xdb;1\xf5Z\xaec?\xae\xf2I\x93\x08\x12\x94\xaa~\x06\x08\x849b")}
+ txnGroups[mod][0].Lsig.Args[0][0]++
+ txnGroups[mod][0].Lsig.Logic = op.Program
+ txnGroups[mod][0].Lsig.Sig = secrets[s].Sign(program)
+ u, _ = binary.Uvarint(txnGroups[mod][0].Txn.Note)
+ badTxnGroups[u] = struct{}{}
+ sv = streamVerifierTestCore(txnGroups, badTxnGroups, errors.New("rejected by logic"), t)
+ sv.WaitForStop()
+ mod++
+
+ _, signedTxn, secrets, addrs = generateTestObjects(numOfTxns, 20, 0, 50)
+ txnGroups = generateTransactionGroups(1, signedTxn, secrets, addrs)
+ badTxnGroups = make(map[uint64]struct{})
+
+ // txn with sig and msig
+ txnGroups[mod][0].Msig = mSigTxn[0].Msig
+ u, _ = binary.Uvarint(txnGroups[mod][0].Txn.Note)
+ badTxnGroups[u] = struct{}{}
+ sv = streamVerifierTestCore(txnGroups, badTxnGroups, errTxnSigNotWellFormed, t)
+ sv.WaitForStop()
+}
+
+// TestStreamVerifierIdel starts the verifer and sends nothing, to trigger the timer, then sends a txn
+func TestStreamVerifierIdel(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ numOfTxns := 1
+ txnGroups, badTxnGroups := getSignedTransactions(numOfTxns, protoMaxGroupSize, 0, 0.5)
+
+ sv := streamVerifierTestCore(txnGroups, badTxnGroups, nil, t)
+ sv.WaitForStop()
+}
+
+func TestGetNumberOfBatchableSigsInGroup(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ numOfTxns := 10
+ txnGroups, _ := getSignedTransactions(numOfTxns, 1, 0, 0)
+ mod := 1
+
+ // txn with 0 sigs
+ txnGroups[mod][0].Sig = crypto.Signature{}
+ batchSigs, err := getNumberOfBatchableSigsInGroup(txnGroups[mod])
+ require.ErrorIs(t, err, errTxnSigHasNoSig)
+ mod++
+
+ _, signedTxns, secrets, addrs := generateTestObjects(numOfTxns, 20, 0, 50)
+ txnGroups = generateTransactionGroups(1, signedTxns, secrets, addrs)
+ batchSigs, err = getNumberOfBatchableSigsInGroup(txnGroups[0])
+ require.NoError(t, err)
+ require.Equal(t, uint64(1), batchSigs)
+
+ // stateproof txn
+ txnGroups[mod][0].Sig = crypto.Signature{}
+ txnGroups[mod][0].Txn.Type = protocol.StateProofTx
+ txnGroups[mod][0].Txn.Header.Sender = transactions.StateProofSender
+ batchSigs, err = getNumberOfBatchableSigsInGroup(txnGroups[mod])
+ require.NoError(t, err)
+ require.Equal(t, uint64(0), batchSigs)
+ mod++
+
+ // multisig
+ _, mSigTxn, _, _ := generateMultiSigTxn(1, 6, 50, t)
+ batchSigs, err = getNumberOfBatchableSigsInGroup(mSigTxn)
+ require.NoError(t, err)
+ require.Equal(t, uint64(2), batchSigs)
+ mod++
+
+ _, signedTxn, secrets, addrs := generateTestObjects(numOfTxns, 20, 0, 50)
+ txnGroups = generateTransactionGroups(1, signedTxn, secrets, addrs)
+
+ // logicsig
+ op, err := logic.AssembleString(`arg 0
+sha256
+byte base64 5rZMNsevs5sULO+54aN+OvU6lQ503z2X+SSYUABIx7E=
+==`)
+ require.NoError(t, err)
+ s := rand.Intn(len(secrets))
+ txnGroups[mod][0].Sig = crypto.Signature{}
+ txnGroups[mod][0].Txn.Sender = addrs[s]
+ txnGroups[mod][0].Lsig.Args = [][]byte{[]byte("=0\x97S\x85H\xe9\x91B\xfd\xdb;1\xf5Z\xaec?\xae\xf2I\x93\x08\x12\x94\xaa~\x06\x08\x849b")}
+ txnGroups[mod][0].Lsig.Logic = op.Program
+ program := logic.Program(op.Program)
+ txnGroups[mod][0].Lsig.Sig = secrets[s].Sign(program)
+ batchSigs, err = getNumberOfBatchableSigsInGroup(txnGroups[mod])
+ require.NoError(t, err)
+ require.Equal(t, uint64(0), batchSigs)
+ mod++
+
+ // txn with sig and msig
+ _, signedTxn, secrets, addrs = generateTestObjects(numOfTxns, 20, 0, 50)
+ txnGroups = generateTransactionGroups(1, signedTxn, secrets, addrs)
+ txnGroups[mod][0].Msig = mSigTxn[0].Msig
+ batchSigs, err = getNumberOfBatchableSigsInGroup(txnGroups[mod])
+ require.ErrorIs(t, err, errTxnSigNotWellFormed)
+}
+
+// TestStreamVerifierPoolShutdown tests what happens when the exec pool shuts down
+func TestStreamVerifierPoolShutdown(t *testing.T) { //nolint:paralleltest // Not parallel because it depends on the default logger
+ partitiontest.PartitionTest(t)
+
+ // only one transaction should be sufficient for the batch verifier
+ // to realize the pool is terminated and to shut down
+ numOfTxns := 1
+ txnGroups, badTxnGroups := getSignedTransactions(numOfTxns, protoMaxGroupSize, 0, 0.5)
+
+ // check the logged information
+ var logBuffer bytes.Buffer
+ log := logging.Base()
+ log.SetOutput(&logBuffer)
+ log.SetLevel(logging.Info)
+
+ // prepare the stream verifier
+ numOfTxnGroups := len(txnGroups)
+ verificationPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, t)
+ _, buffLen := verificationPool.BufferSize()
+
+ // make sure the pool is shut down and the buffer is full
+ holdTasks := make(chan interface{})
+ for x := 0; x < buffLen+runtime.NumCPU(); x++ {
+ verificationPool.EnqueueBacklog(context.Background(),
+ func(arg interface{}) interface{} { <-holdTasks; return nil }, nil, nil)
+ }
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ // Shutdown will block until all tasks held by holdTasks is released
+ verificationPool.Shutdown()
+ }()
+ // Send more tasks to break the backlog worker after b.pool.Enqueue returns the error
+ for x := 0; x < 100; x++ {
+ verificationPool.EnqueueBacklog(context.Background(),
+ func(arg interface{}) interface{} { <-holdTasks; return nil }, nil, nil)
+ }
+ // release the tasks
+ close(holdTasks)
+
+ // make sure the EnqueueBacklogis returning err
+ for x := 0; x < 10; x++ {
+ err := verificationPool.EnqueueBacklog(context.Background(),
+ func(arg interface{}) interface{} { return nil }, nil, nil)
+ require.Error(t, err, fmt.Sprintf("x = %d", x))
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ cache := MakeVerifiedTransactionCache(50000)
+
+ stxnChan := make(chan *UnverifiedElement)
+ resultChan := make(chan *VerificationResult, txBacklogSize)
+ droppedChan := make(chan *UnverifiedElement)
+ sv, err := MakeStreamVerifier(stxnChan, resultChan, droppedChan, &DummyLedgerForSignature{}, verificationPool, cache)
+ require.NoError(t, err)
+ sv.Start(ctx)
+
+ errChan := make(chan error)
+
+ var badSigResultCounter int
+ var goodSigResultCounter int
+
+ wg.Add(1)
+ go processResults(ctx, errChan, resultChan, numOfTxnGroups, badTxnGroups, &badSigResultCounter, &goodSigResultCounter, &wg)
+
+ // When the exec pool shuts down, the batch verifier should gracefully stop
+ // cancel the context so that the test can terminate
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ sv.WaitForStop()
+ cancel()
+ }()
+
+ wg.Add(1)
+ // send txn groups to be verified
+ go func() {
+ defer wg.Done()
+ for _, tg := range txnGroups {
+ select {
+ case <-ctx.Done():
+ break
+ case stxnChan <- &UnverifiedElement{TxnGroup: tg, BacklogMessage: nil}:
+ }
+ }
+ }()
+ for err := range errChan {
+ require.ErrorIs(t, err, errShuttingDownError)
+ }
+ require.Contains(t, logBuffer.String(), "addVerificationTaskToThePoolNow: EnqueueBacklog returned an error and StreamVerifier will stop: context canceled")
+}
+
+// TestStreamVerifierRestart tests what happens when the context is canceled
+func TestStreamVerifierRestart(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ numOfTxns := 1000
+ txnGroups, badTxnGroups := getSignedTransactions(numOfTxns, 1, 0, 0.5)
+
+ // prepare the stream verifier
+ numOfTxnGroups := len(txnGroups)
+ verificationPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, t)
+ defer verificationPool.Shutdown()
+
+ cache := MakeVerifiedTransactionCache(50)
+
+ stxnChan := make(chan *UnverifiedElement)
+ resultChan := make(chan *VerificationResult, txBacklogSize)
+ droppedChan := make(chan *UnverifiedElement)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ sv, err := MakeStreamVerifier(stxnChan, resultChan, droppedChan, &DummyLedgerForSignature{}, verificationPool, cache)
+ require.NoError(t, err)
+ sv.Start(ctx)
+
+ errChan := make(chan error)
+
+ var badSigResultCounter int
+ var goodSigResultCounter int
+
+ ctx2, cancel2 := context.WithCancel(context.Background())
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ go processResults(ctx2, errChan, resultChan, numOfTxnGroups, badTxnGroups, &badSigResultCounter, &goodSigResultCounter, &wg)
+
+ wg.Add(1)
+ // send txn groups to be verified
+ go func() {
+ defer wg.Done()
+ for i, tg := range txnGroups {
+ if (i+1)%10 == 0 {
+ cancel()
+ sv.WaitForStop()
+ ctx, cancel = context.WithCancel(context.Background())
+ sv.Start(ctx)
+ }
+ select {
+ case <-ctx2.Done():
+ break
+ case stxnChan <- &UnverifiedElement{TxnGroup: tg, BacklogMessage: nil}:
+ }
+ }
+ cancel()
+ }()
+ for err := range errChan {
+ require.ErrorIs(t, err, errShuttingDownError)
+ }
+ wg.Wait()
+ sv.WaitForStop()
+ cancel2() // not necessary, but the golint will want to see this
+}
+
+// TestBlockWatcher runs multiple goroutines to check the concurency and correctness of the block watcher
+func TestStreamVerifierBlockWatcher(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ blkHdr := createDummyBlockHeader()
+ nbw := MakeNewBlockWatcher(blkHdr)
+ startingRound := blkHdr.Round
+
+ wg := sync.WaitGroup{}
+ count := 100
+
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for x := 0; x < 100; x++ {
+ blkHdr.Round++
+ nbw.OnNewBlock(bookkeeping.Block{BlockHeader: blkHdr}, ledgercore.StateDelta{})
+ time.Sleep(10 * time.Millisecond)
+ nbw.OnNewBlock(bookkeeping.Block{BlockHeader: blkHdr}, ledgercore.StateDelta{})
+ }
+ }()
+
+ bhStore := make(map[basics.Round]*bookkeeping.BlockHeader)
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for {
+ bh := nbw.getBlockHeader()
+ bhStore[bh.Round] = bh
+ if bh.Round == startingRound+10 {
+ break
+ }
+ }
+ }()
+ wg.Wait()
+ bh := nbw.getBlockHeader()
+ require.Equal(t, uint64(startingRound)+uint64(count), uint64(bh.Round))
+ // There should be no inconsistency after new blocks are added
+ for r, bh := range bhStore {
+ require.Equal(t, r, bh.Round)
+ }
+}
+
+func getSaturatedExecPool(t *testing.T) (execpool.BacklogPool, chan interface{}) {
+ verificationPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, t)
+ _, buffLen := verificationPool.BufferSize()
+
+ // make the buffer full to control when the tasks get executed
+ holdTasks := make(chan interface{})
+ for x := 0; x < buffLen+runtime.NumCPU()+1; x++ {
+ verificationPool.EnqueueBacklog(context.Background(),
+ func(arg interface{}) interface{} {
+ <-holdTasks
+ return nil
+ }, nil, nil)
+ }
+ return verificationPool, holdTasks
+}
+
+// TestStreamVerifierCtxCancel tests the termination when the ctx is canceled
+// To make sure that the batchingLoop is still working on a batch when the
+// ctx is cancled, this test first saturates the exec pool buffer, then
+// sends a txn and immediately cancels the ctx so that the batch is not
+// passed to the exec pool yet, but is in batchingLoop
+func TestStreamVerifierCtxCancel(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ verificationPool, holdTasks := getSaturatedExecPool(t)
+ defer verificationPool.Shutdown()
+ ctx, cancel := context.WithCancel(context.Background())
+ cache := MakeVerifiedTransactionCache(50)
+ stxnChan := make(chan *UnverifiedElement)
+ resultChan := make(chan *VerificationResult, txBacklogSize)
+ droppedChan := make(chan *UnverifiedElement)
+ sv, err := MakeStreamVerifier(stxnChan, resultChan, droppedChan, &DummyLedgerForSignature{}, verificationPool, cache)
+ require.NoError(t, err)
+ sv.Start(ctx)
+
+ var result *VerificationResult
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ // no verification tasks should be executed
+ // one result should be returned
+ result = <-resultChan
+ }()
+
+ // send batchSizeBlockLimit after the exec pool buffer is full
+ numOfTxns := 1
+ txnGroups, _ := getSignedTransactions(numOfTxns, 1, 0, 0.5)
+ stxnChan <- &UnverifiedElement{TxnGroup: txnGroups[0], BacklogMessage: nil}
+ // cancel the ctx before the sig is sent to the exec pool
+ cancel()
+
+ // the main loop should stop after cancel()
+ sv.WaitForStop()
+
+ // release the tasks
+ close(holdTasks)
+
+ wg.Wait()
+ require.ErrorIs(t, result.Err, errShuttingDownError)
+}
+
+// TestStreamVerifierCtxCancelPoolQueue tests the termination when the ctx is canceled
+// To make sure that the batchingLoop is still working on a batch when the
+// ctx is cancled, this test first saturates the exec pool buffer, then
+// sends a txn and cancels the ctx after multiple waitForNextTxnDuration
+// so that the batch is sent to the pool. Since the pool is saturated,
+// the task will be stuck waiting to be queued when the context is canceled
+// everything should be gracefully terminated
+func TestStreamVerifierCtxCancelPoolQueue(t *testing.T) { //nolint:paralleltest // Not parallel because it depends on the default logger
+ partitiontest.PartitionTest(t)
+
+ verificationPool, holdTasks := getSaturatedExecPool(t)
+
+ // check the logged information
+ var logBuffer bytes.Buffer
+ log := logging.Base()
+ log.SetOutput(&logBuffer)
+ log.SetLevel(logging.Info)
+
+ ctx, cancel := context.WithCancel(context.Background())
+ cache := MakeVerifiedTransactionCache(50)
+ stxnChan := make(chan *UnverifiedElement)
+ resultChan := make(chan *VerificationResult, txBacklogSize)
+ droppedChan := make(chan *UnverifiedElement)
+ sv, err := MakeStreamVerifier(stxnChan, resultChan, droppedChan, &DummyLedgerForSignature{}, verificationPool, cache)
+ require.NoError(t, err)
+ sv.Start(ctx)
+
+ var result *VerificationResult
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for {
+ result = <-resultChan
+ // at least one errShuttingDownError is expected
+ if result.Err != errShuttingDownError {
+ continue
+ }
+ break
+ }
+ }()
+
+ // send batchSizeBlockLimit after the exec pool buffer is full
+ numOfTxns := 1
+ txnGroups, _ := getSignedTransactions(numOfTxns, 1, 0, 0.5)
+
+ wg.Add(1)
+ // run in separate goroutine because the exec pool is blocked here, and this will not advance
+ // until holdTasks are closed
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ // Normally, a single txn is sufficient, but the goroutines could be scheduled is such a way that
+ // the single transaction slips through and passes the batch verifier before the exec pool shuts down.
+ // this happens when close(holdTasks) runs and frees the exec pool, and lets the txns get verified, before
+ // verificationPool.Shutdown() executes.
+ case stxnChan <- &UnverifiedElement{TxnGroup: txnGroups[0], BacklogMessage: nil}:
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+ // cancel the ctx as the sig is not yet sent to the exec pool
+ // the test might sporadically fail if between sending the txn above
+ // and the cancelation, 2 x waitForNextTxnDuration elapses (10ms)
+ time.Sleep(6 * waitForNextTxnDuration)
+ go func() {
+ // wait a bit before releasing the tasks, so that the verificationPool ctx first gets canceled
+ time.Sleep(20 * time.Millisecond)
+ close(holdTasks)
+ }()
+ verificationPool.Shutdown()
+
+ // the main loop should stop before calling cancel() when the exec pool shuts down and returns an error
+ sv.WaitForStop()
+ cancel()
+
+ wg.Wait()
+ require.ErrorIs(t, result.Err, errShuttingDownError)
+ require.Contains(t, logBuffer.String(), "addVerificationTaskToThePoolNow: EnqueueBacklog returned an error and StreamVerifier will stop: context canceled")
+}
+
+// TestStreamVerifierPostVBlocked tests the behavior when the return channel (result chan) of verified
+// transactions is blocked, and checks droppedFromPool counter to confirm the drops
+func TestStreamVerifierPostVBlocked(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ // prepare the stream verifier
+ verificationPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, t)
+ defer verificationPool.Shutdown()
+ errChan := make(chan error)
+ var badSigResultCounter int
+ var goodSigResultCounter int
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ cache := MakeVerifiedTransactionCache(50)
+
+ txBacklogSizeMod := txBacklogSize / 20
+
+ stxnChan := make(chan *UnverifiedElement)
+ resultChan := make(chan *VerificationResult, txBacklogSizeMod)
+ droppedChan := make(chan *UnverifiedElement)
+ sv, err := MakeStreamVerifier(stxnChan, resultChan, droppedChan, &DummyLedgerForSignature{}, verificationPool, cache)
+ require.NoError(t, err)
+
+ defer close(droppedChan)
+ go func() {
+ for range droppedChan {
+ droppedFromPool.Inc(nil)
+ }
+ }()
+
+ // start the verifier
+ sv.Start(ctx)
+ overflow := 3
+ // send txBacklogSizeMod + 3 transactions to overflow the result buffer
+ numOfTxns := txBacklogSizeMod + overflow
+ txnGroups, badTxnGroups := getSignedTransactions(numOfTxns, 1, 0, 0.5)
+ numOfTxnGroups := len(txnGroups)
+ for _, tg := range txnGroups {
+ stxnChan <- &UnverifiedElement{TxnGroup: tg, BacklogMessage: nil}
+ }
+
+ var droppedPool uint64
+ // wait until overflow transactions are dropped
+ for w := 0; w < 100; w++ {
+ droppedPool = droppedFromPool.GetUint64Value()
+ if droppedPool >= uint64(overflow) {
+ break
+ }
+ time.Sleep(time.Millisecond * 20)
+ }
+
+ require.Equal(t, uint64(overflow), droppedPool)
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ // make sure the other results are fine
+ go processResults(ctx, errChan, resultChan, numOfTxnGroups-overflow, badTxnGroups, &badSigResultCounter, &goodSigResultCounter, &wg)
+
+ for err := range errChan {
+ require.ErrorIs(t, err, errShuttingDownError)
+ fmt.Println(badTxnGroups)
+ }
+
+ // check if more transactions can be accepted
+ errChan = make(chan error)
+
+ wg.Add(1)
+ // make sure the other results are fine
+ txnGroups, badTxnGroups2 := getSignedTransactions(numOfTxns, 1, numOfTxns, 0.5)
+ // need to combine these, since left overs from the previous one could still come out
+ for b := range badTxnGroups2 {
+ badTxnGroups[b] = struct{}{}
+ }
+ go processResults(ctx, errChan, resultChan, numOfTxnGroups, badTxnGroups, &badSigResultCounter, &goodSigResultCounter, &wg)
+
+ for _, tg := range txnGroups {
+ stxnChan <- &UnverifiedElement{TxnGroup: tg, BacklogMessage: nil}
+ }
+
+ for err := range errChan {
+ require.ErrorIs(t, err, errShuttingDownError)
+ fmt.Println(badTxnGroups)
+ }
+
+ wg.Wait()
+}
+
+func TestStreamVerifierMakeStreamVerifierErr(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ _, err := MakeStreamVerifier(nil, nil, nil, &DummyLedgerForSignature{badHdr: true}, nil, nil)
+ require.Error(t, err)
+}
+
+// TestStreamVerifierCancelWhenPooled tests the case where the ctx is cancled after the verification
+// task is queued to the exec pool and before the task is executed in the pool
+func TestStreamVerifierCancelWhenPooled(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ numOfTxns := 1000
+ txnGroups, badTxnGroups := getSignedTransactions(numOfTxns, 1, 0, 0.5)
+
+ // prepare the stream verifier
+ numOfTxnGroups := len(txnGroups)
+ execPool := execpool.MakePool(t)
+ defer execPool.Shutdown()
+ verificationPool := execpool.MakeBacklog(execPool, 64, execpool.LowPriority, t)
+ defer verificationPool.Shutdown()
+
+ cache := MakeVerifiedTransactionCache(50)
+
+ stxnChan := make(chan *UnverifiedElement)
+ resultChan := make(chan *VerificationResult, txBacklogSize)
+ droppedChan := make(chan *UnverifiedElement)
+ ctx, cancel := context.WithCancel(context.Background())
+ sv, err := MakeStreamVerifier(stxnChan, resultChan, droppedChan, &DummyLedgerForSignature{}, verificationPool, cache)
+ require.NoError(t, err)
+ sv.Start(ctx)
+
+ errChan := make(chan error)
+
+ var badSigResultCounter int
+ var goodSigResultCounter int
+
+ ctx2, cancel2 := context.WithCancel(context.Background())
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+ go processResults(ctx2, errChan, resultChan, numOfTxnGroups, badTxnGroups, &badSigResultCounter, &goodSigResultCounter, &wg)
+
+ wg.Add(1)
+ // send txn groups to be verified
+ go func() {
+ defer wg.Done()
+ for _, tg := range txnGroups {
+ stxnChan <- &UnverifiedElement{TxnGroup: tg, BacklogMessage: nil}
+ }
+ // cancel the ctx, and expect at least one task queued to the pool but not yet executed
+ cancel()
+ }()
+ for err := range errChan {
+ require.ErrorIs(t, err, errShuttingDownError)
+ }
+ wg.Wait()
+ sv.WaitForStop()
+ cancel2() // not necessary, but the golint will want to see this
+}
diff --git a/data/transactions/verify/verifiedTxnCache.go b/data/transactions/verify/verifiedTxnCache.go
index 82f0e9772..efa470150 100644
--- a/data/transactions/verify/verifiedTxnCache.go
+++ b/data/transactions/verify/verifiedTxnCache.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -59,7 +59,7 @@ type VerifiedTransactionCache interface {
// in the cache, the new entry overrides the old one.
Add(txgroup []transactions.SignedTxn, groupCtx *GroupContext)
// AddPayset works in a similar way to Add, but is intended for adding an array of transaction groups, along with their corresponding contexts.
- AddPayset(txgroup [][]transactions.SignedTxn, groupCtxs []*GroupContext) error
+ AddPayset(txgroup [][]transactions.SignedTxn, groupCtxs []*GroupContext)
// GetUnverifiedTransactionGroups compares the provided payset against the currently cached transactions and figure which transaction groups aren't fully cached.
GetUnverifiedTransactionGroups(payset [][]transactions.SignedTxn, CurrSpecAddrs transactions.SpecialAddresses, CurrProto protocol.ConsensusVersion) [][]transactions.SignedTxn
// UpdatePinned replaces the pinned entries with the one provided in the pinnedTxns map. This is typically expected to be a subset of the
@@ -106,13 +106,12 @@ func (v *verifiedTransactionCache) Add(txgroup []transactions.SignedTxn, groupCt
}
// AddPayset works in a similar way to Add, but is intended for adding an array of transaction groups, along with their corresponding contexts.
-func (v *verifiedTransactionCache) AddPayset(txgroup [][]transactions.SignedTxn, groupCtxs []*GroupContext) error {
+func (v *verifiedTransactionCache) AddPayset(txgroup [][]transactions.SignedTxn, groupCtxs []*GroupContext) {
v.bucketsLock.Lock()
defer v.bucketsLock.Unlock()
for i := range txgroup {
v.add(txgroup[i], groupCtxs[i])
}
- return nil
}
// GetUnverifiedTransactionGroups compares the provided payset against the currently cached transactions and figure which transaction groups aren't fully cached.
@@ -268,8 +267,7 @@ func (v *mockedCache) Add(txgroup []transactions.SignedTxn, groupCtx *GroupConte
return
}
-func (v *mockedCache) AddPayset(txgroup [][]transactions.SignedTxn, groupCtxs []*GroupContext) error {
- return nil
+func (v *mockedCache) AddPayset(txgroup [][]transactions.SignedTxn, groupCtxs []*GroupContext) {
}
func (v *mockedCache) GetUnverifiedTransactionGroups(txnGroups [][]transactions.SignedTxn, currSpecAddrs transactions.SpecialAddresses, currProto protocol.ConsensusVersion) (unverifiedGroups [][]transactions.SignedTxn) {
diff --git a/data/transactions/verify/verifiedTxnCache_test.go b/data/transactions/verify/verifiedTxnCache_test.go
index e3001db67..b90eb66ef 100644
--- a/data/transactions/verify/verifiedTxnCache_test.go
+++ b/data/transactions/verify/verifiedTxnCache_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -32,8 +32,8 @@ func TestAddingToCache(t *testing.T) {
icache := MakeVerifiedTransactionCache(500)
impl := icache.(*verifiedTransactionCache)
- _, signedTxn, secrets, addrs := generateTestObjects(10, 5, 50)
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ _, signedTxn, secrets, addrs := generateTestObjects(10, 5, 0, 50)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
groupCtx, err := PrepareGroupContext(txnGroups[0], blockHeader, nil)
require.NoError(t, err)
impl.Add(txnGroups[0], groupCtx)
@@ -52,7 +52,7 @@ func TestBucketCycling(t *testing.T) {
entriesPerBucket := 100
icache := MakeVerifiedTransactionCache(entriesPerBucket * (bucketCount - 1))
impl := icache.(*verifiedTransactionCache)
- _, signedTxn, _, _ := generateTestObjects(entriesPerBucket*bucketCount*2, bucketCount, 0)
+ _, signedTxn, _, _ := generateTestObjects(entriesPerBucket*bucketCount*2, bucketCount, 0, 0)
require.Equal(t, entriesPerBucket*bucketCount*2, len(signedTxn))
groupCtx, err := PrepareGroupContext([]transactions.SignedTxn{signedTxn[0]}, blockHeader, nil)
@@ -82,8 +82,8 @@ func TestGetUnverifiedTransactionGroups50(t *testing.T) {
size := 300
icache := MakeVerifiedTransactionCache(size * 2)
impl := icache.(*verifiedTransactionCache)
- _, signedTxn, secrets, addrs := generateTestObjects(size*2, 10+size/1000, 0)
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ _, signedTxn, secrets, addrs := generateTestObjects(size*2, 10+size/1000, 0, 0)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
expectedUnverifiedGroups := make([][]transactions.SignedTxn, 0, len(txnGroups)/2)
// add every even transaction to the cache.
@@ -107,8 +107,8 @@ func BenchmarkGetUnverifiedTransactionGroups50(b *testing.B) {
}
icache := MakeVerifiedTransactionCache(b.N * 2)
impl := icache.(*verifiedTransactionCache)
- _, signedTxn, secrets, addrs := generateTestObjects(b.N*2, 10+b.N/1000, 0)
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ _, signedTxn, secrets, addrs := generateTestObjects(b.N*2, 10+b.N/1000, 0, 0)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
queryTxnGroups := make([][]transactions.SignedTxn, 0, b.N)
// add every even transaction to the cache.
@@ -140,8 +140,8 @@ func TestUpdatePinned(t *testing.T) {
size := 100
icache := MakeVerifiedTransactionCache(size * 10)
impl := icache.(*verifiedTransactionCache)
- _, signedTxn, secrets, addrs := generateTestObjects(size*2, 10, 0)
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ _, signedTxn, secrets, addrs := generateTestObjects(size*2, 10, 0, 0)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
// insert some entries.
for i := 0; i < len(txnGroups); i++ {
@@ -169,8 +169,8 @@ func TestPinningTransactions(t *testing.T) {
size := 100
icache := MakeVerifiedTransactionCache(size)
impl := icache.(*verifiedTransactionCache)
- _, signedTxn, secrets, addrs := generateTestObjects(size*2, 10, 0)
- txnGroups := generateTransactionGroups(signedTxn, secrets, addrs)
+ _, signedTxn, secrets, addrs := generateTestObjects(size*2, 10, 0, 0)
+ txnGroups := generateTransactionGroups(protoMaxGroupSize, signedTxn, secrets, addrs)
// insert half of the entries.
for i := 0; i < len(txnGroups)/2; i++ {
diff --git a/data/txDupCache.go b/data/txDupCache.go
index 026a16857..96a426dc7 100644
--- a/data/txDupCache.go
+++ b/data/txDupCache.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -109,6 +109,7 @@ type txSaltedCache struct {
curSalt [4]byte
prevSalt [4]byte
ctx context.Context
+ wg sync.WaitGroup
}
func makeSaltedCache(size int) *txSaltedCache {
@@ -117,9 +118,10 @@ func makeSaltedCache(size int) *txSaltedCache {
}
}
-func (c *txSaltedCache) start(ctx context.Context, refreshInterval time.Duration) {
+func (c *txSaltedCache) Start(ctx context.Context, refreshInterval time.Duration) {
c.ctx = ctx
if refreshInterval != 0 {
+ c.wg.Add(1)
go c.salter(refreshInterval)
}
@@ -128,9 +130,14 @@ func (c *txSaltedCache) start(ctx context.Context, refreshInterval time.Duration
c.moreSalt()
}
+func (c *txSaltedCache) WaitForStop() {
+ c.wg.Wait()
+}
+
// salter is a goroutine refreshing the cache by schedule
func (c *txSaltedCache) salter(refreshInterval time.Duration) {
ticker := time.NewTicker(refreshInterval)
+ defer c.wg.Done()
defer ticker.Stop()
for {
select {
@@ -221,6 +228,12 @@ func (c *txSaltedCache) CheckAndPut(msg []byte) (*crypto.Digest, bool) {
// already added to cache between RUnlock() and Lock(), return
return d, found
}
+ } else {
+ // Do another check to see if another copy of the transaction won the race to write it to the cache
+ // Only check current to save a lookup since swaps are rare and no need to re-hash
+ if _, found := c.cur[*d]; found {
+ return d, found
+ }
}
if len(c.cur) >= c.maxSize {
diff --git a/data/txDupCache_test.go b/data/txDupCache_test.go
index e0bfac25a..13a36b19d 100644
--- a/data/txDupCache_test.go
+++ b/data/txDupCache_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -120,7 +120,7 @@ func TestTxHandlerSaltedCacheBasic(t *testing.T) {
const size = 20
cache := makeSaltedCache(size)
- cache.start(context.Background(), 0)
+ cache.Start(context.Background(), 0)
require.Zero(t, cache.Len())
// add some unique random
@@ -204,7 +204,7 @@ func TestTxHandlerSaltedCacheScheduled(t *testing.T) {
const size = 20
updateInterval := 1000 * time.Microsecond
cache := makeSaltedCache(size)
- cache.start(context.Background(), updateInterval)
+ cache.Start(context.Background(), updateInterval)
require.Zero(t, cache.Len())
// add some unique random
@@ -229,7 +229,7 @@ func TestTxHandlerSaltedCacheManual(t *testing.T) {
const size = 20
cache := makeSaltedCache(2 * size)
- cache.start(context.Background(), 0)
+ cache.Start(context.Background(), 0)
require.Zero(t, cache.Len())
// add some unique random
@@ -294,7 +294,7 @@ func (m digestCacheMaker) make(size int) cachePusher {
}
func (m saltedCacheMaker) make(size int) cachePusher {
scp := &saltedCachePusher{c: makeSaltedCache(size)}
- scp.c.start(context.Background(), 0)
+ scp.c.Start(context.Background(), 0)
return scp
}
diff --git a/data/txHandler.go b/data/txHandler.go
index ade8b8018..66cac800a 100644
--- a/data/txHandler.go
+++ b/data/txHandler.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -35,16 +35,11 @@ import (
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/network"
"github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/util"
"github.com/algorand/go-algorand/util/execpool"
"github.com/algorand/go-algorand/util/metrics"
)
-// The size txBacklogSize used to determine the size of the backlog that is used to store incoming transaction messages before starting dropping them.
-// It should be configured to be higher then the number of CPU cores, so that the execution pool get saturated, but not too high to avoid lockout of the
-// execution pool for a long duration of time.
-// Set backlog at 'approximately one block' by dividing block size by a typical transaction size.
-var txBacklogSize = config.Consensus[protocol.ConsensusCurrentVersion].MaxTxnBytesPerBlock / 200
-
var transactionMessagesHandled = metrics.MakeCounter(metrics.TransactionMessagesHandled)
var transactionMessagesDroppedFromBacklog = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromBacklog)
var transactionMessagesDroppedFromPool = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromPool)
@@ -66,6 +61,13 @@ var transactionMessagesBacklogSizeGauge = metrics.MakeGauge(metrics.TransactionM
var transactionGroupTxSyncHandled = metrics.MakeCounter(metrics.TransactionGroupTxSyncHandled)
var transactionGroupTxSyncRemember = metrics.MakeCounter(metrics.TransactionGroupTxSyncRemember)
var transactionGroupTxSyncAlreadyCommitted = metrics.MakeCounter(metrics.TransactionGroupTxSyncAlreadyCommitted)
+var txBacklogDroppedCongestionManagement = metrics.MakeCounter(metrics.TransactionMessagesTxnDroppedCongestionManagement)
+
+// ErrInvalidTxPool is reported when nil is passed for the tx pool
+var ErrInvalidTxPool = errors.New("MakeTxHandler: txPool is nil on initialization")
+
+// ErrInvalidLedger is reported when nil is passed for the ledger
+var ErrInvalidLedger = errors.New("MakeTxHandler: ledger is nil on initialization")
var transactionMessageTxPoolRememberCounter = metrics.NewTagCounter(
"algod_transaction_messages_txpool_remember_err_{TAG}", "Number of transaction messages not remembered by txpool b/c of {TAG}",
@@ -104,6 +106,7 @@ type txBacklogMsg struct {
rawmsgDataHash *crypto.Digest // hash (if any) of raw message data from the network
unverifiedTxGroupHash *crypto.Digest // hash (if any) of the unverifiedTxGroup
verificationErr error // The verification error generated by the verification function, if any.
+ capguard *util.ErlCapacityGuard // the structure returned from the elastic rate limiter, to be released when dequeued
}
// TxHandler handles transaction messages
@@ -114,7 +117,7 @@ type TxHandler struct {
genesisHash crypto.Digest
txVerificationPool execpool.BacklogPool
backlogQueue chan *txBacklogMsg
- postVerificationQueue chan *txBacklogMsg
+ postVerificationQueue chan *verify.VerificationResult
backlogWg sync.WaitGroup
net network.GossipNode
msgCache *txSaltedCache
@@ -122,6 +125,10 @@ type TxHandler struct {
cacheConfig txHandlerConfig
ctx context.Context
ctxCancel context.CancelFunc
+ streamVerifier *verify.StreamVerifier
+ streamVerifierChan chan *verify.UnverifiedElement
+ streamVerifierDropped chan *verify.UnverifiedElement
+ erl *util.ElasticRateLimiter
}
// TxHandlerOpts is TxHandler configuration options
@@ -142,16 +149,20 @@ type txHandlerConfig struct {
}
// MakeTxHandler makes a new handler for transaction messages
-func MakeTxHandler(opts TxHandlerOpts) *TxHandler {
+func MakeTxHandler(opts TxHandlerOpts) (*TxHandler, error) {
if opts.TxPool == nil {
- logging.Base().Fatal("MakeTxHandler: txPool is nil on initialization")
- return nil
+ return nil, ErrInvalidTxPool
}
if opts.Ledger == nil {
- logging.Base().Fatal("MakeTxHandler: ledger is nil on initialization")
- return nil
+ return nil, ErrInvalidLedger
+ }
+
+ // backlog size is big enough for each peer to have its reserved capacity in the backlog, plus the config's backlogSize as a shared capacity
+ txBacklogSize := opts.Config.TxBacklogSize
+ if opts.Config.EnableTxBacklogRateLimiting {
+ txBacklogSize += (opts.Config.IncomingConnectionsLimit * opts.Config.TxBacklogReservedCapacityPerPeer)
}
handler := &TxHandler{
@@ -161,31 +172,75 @@ func MakeTxHandler(opts TxHandlerOpts) *TxHandler {
ledger: opts.Ledger,
txVerificationPool: opts.ExecutionPool,
backlogQueue: make(chan *txBacklogMsg, txBacklogSize),
- postVerificationQueue: make(chan *txBacklogMsg, txBacklogSize),
+ postVerificationQueue: make(chan *verify.VerificationResult, txBacklogSize),
net: opts.Net,
msgCache: makeSaltedCache(2 * txBacklogSize),
txCanonicalCache: makeDigestCache(2 * txBacklogSize),
cacheConfig: txHandlerConfig{opts.Config.TxFilterRawMsgEnabled(), opts.Config.TxFilterCanonicalEnabled()},
+ streamVerifierChan: make(chan *verify.UnverifiedElement),
+ streamVerifierDropped: make(chan *verify.UnverifiedElement),
+ }
+
+ if opts.Config.EnableTxBacklogRateLimiting {
+ rateLimiter := util.NewElasticRateLimiter(
+ txBacklogSize,
+ opts.Config.TxBacklogReservedCapacityPerPeer,
+ time.Duration(opts.Config.TxBacklogServiceRateWindowSeconds)*time.Second,
+ txBacklogDroppedCongestionManagement,
+ )
+ handler.erl = rateLimiter
+ }
+
+ // prepare the transaction stream verifer
+ var err error
+ handler.streamVerifier, err = verify.MakeStreamVerifier(handler.streamVerifierChan,
+ handler.postVerificationQueue, handler.streamVerifierDropped, handler.ledger,
+ handler.txVerificationPool, handler.ledger.VerifiedTransactionCache())
+ if err != nil {
+ return nil, err
+ }
+ go handler.droppedTxnWatcher()
+ return handler, nil
+}
+
+func (handler *TxHandler) droppedTxnWatcher() {
+ for unverified := range handler.streamVerifierDropped {
+ // we failed to write to the output queue, since the queue was full.
+ // adding the metric here allows us to monitor how frequently it happens.
+ transactionMessagesDroppedFromPool.Inc(nil)
+
+ tx := unverified.BacklogMessage.(*txBacklogMsg)
+
+ // delete from duplicate caches to give it a chance to be re-submitted
+ handler.deleteFromCaches(tx.rawmsgDataHash, tx.unverifiedTxGroupHash)
}
- return handler
}
// Start enables the processing of incoming messages at the transaction handler
func (handler *TxHandler) Start() {
handler.ctx, handler.ctxCancel = context.WithCancel(context.Background())
- handler.msgCache.start(handler.ctx, 60*time.Second)
+ handler.msgCache.Start(handler.ctx, 60*time.Second)
handler.net.RegisterHandlers([]network.TaggedMessageHandler{
{Tag: protocol.TxnTag, MessageHandler: network.HandlerFunc(handler.processIncomingTxn)},
})
handler.backlogWg.Add(2)
go handler.backlogWorker()
go handler.backlogGaugeThread()
+ handler.streamVerifier.Start(handler.ctx)
+ if handler.erl != nil {
+ handler.erl.Start()
+ }
}
// Stop suspends the processing of incoming messages at the transaction handler
func (handler *TxHandler) Stop() {
handler.ctxCancel()
+ if handler.erl != nil {
+ handler.erl.Stop()
+ }
handler.backlogWg.Wait()
+ handler.streamVerifier.WaitForStop()
+ handler.msgCache.WaitForStop()
}
func reencode(stxns []transactions.SignedTxn) []byte {
@@ -203,7 +258,7 @@ func (handler *TxHandler) backlogGaugeThread() {
for {
select {
case <-ticker.C:
- transactionMessagesBacklogSizeGauge.Set(float64(len(handler.backlogQueue)))
+ transactionMessagesBacklogSizeGauge.Set(uint64(len(handler.backlogQueue)))
case <-handler.ctx.Done():
return
}
@@ -223,7 +278,9 @@ func (handler *TxHandler) backlogWorker() {
if !ok {
return
}
- handler.postProcessCheckedTxn(wi)
+ m := wi.BacklogMessage.(*txBacklogMsg)
+ m.verificationErr = wi.Err
+ handler.postProcessCheckedTxn(m)
// restart the loop so that we could empty out the post verification queue.
continue
@@ -234,21 +291,39 @@ func (handler *TxHandler) backlogWorker() {
select {
case wi, ok := <-handler.backlogQueue:
if !ok {
+ // this is never happening since handler.backlogQueue is never closed
return
}
+ if wi.capguard != nil {
+ if err := wi.capguard.Release(); err != nil {
+ logging.Base().Warnf("Failed to release capacity to ElasticRateLimiter: %v", err)
+ }
+ }
if handler.checkAlreadyCommitted(wi) {
transactionMessagesAlreadyCommitted.Inc(nil)
+ if wi.capguard != nil {
+ wi.capguard.Served()
+ }
continue
}
-
- // enqueue the task to the verification pool.
- handler.txVerificationPool.EnqueueBacklog(handler.ctx, handler.asyncVerifySignature, wi, nil)
-
+ // handler.streamVerifierChan does not receive if ctx is cancled
+ select {
+ case handler.streamVerifierChan <- &verify.UnverifiedElement{TxnGroup: wi.unverifiedTxGroup, BacklogMessage: wi}:
+ case <-handler.ctx.Done():
+ transactionMessagesDroppedFromBacklog.Inc(nil)
+ return
+ }
+ if wi.capguard != nil {
+ wi.capguard.Served()
+ }
case wi, ok := <-handler.postVerificationQueue:
if !ok {
+ // this is never happening since handler.postVerificationQueue is never closed
return
}
- handler.postProcessCheckedTxn(wi)
+ m := wi.BacklogMessage.(*txBacklogMsg)
+ m.verificationErr = wi.Err
+ handler.postProcessCheckedTxn(m)
case <-handler.ctx.Done():
return
@@ -257,7 +332,7 @@ func (handler *TxHandler) backlogWorker() {
}
func (handler *TxHandler) postProcessReportErrors(err error) {
- if errors.Is(err, crypto.ErrBatchVerificationFailed) {
+ if errors.Is(err, crypto.ErrBatchHasFailedSigs) {
transactionMessagesTxnSigVerificationFailed.Inc(nil)
return
}
@@ -421,36 +496,6 @@ func (handler *TxHandler) postProcessCheckedTxn(wi *txBacklogMsg) {
handler.net.Relay(handler.ctx, protocol.TxnTag, reencode(verifiedTxGroup), false, wi.rawmsg.Sender)
}
-// asyncVerifySignature verifies that the given transaction group is valid, and update the txBacklogMsg data structure accordingly.
-func (handler *TxHandler) asyncVerifySignature(arg interface{}) interface{} {
- tx := arg.(*txBacklogMsg)
-
- // build the transaction verification context
- latest := handler.ledger.Latest()
- latestHdr, err := handler.ledger.BlockHdr(latest)
- if err != nil {
- tx.verificationErr = fmt.Errorf("could not get header for previous block %d: %w", latest, err)
- logging.Base().Warnf("Could not get header for previous block %d: %v", latest, err)
- } else {
- // we can't use PaysetGroups here since it's using a execpool like this go-routine and we don't want to deadlock.
- _, tx.verificationErr = verify.TxnGroup(tx.unverifiedTxGroup, latestHdr, handler.ledger.VerifiedTransactionCache(), handler.ledger)
- }
-
- select {
- case handler.postVerificationQueue <- tx:
- default:
- // we failed to write to the output queue, since the queue was full.
- // adding the metric here allows us to monitor how frequently it happens.
- transactionMessagesDroppedFromPool.Inc(nil)
-
- // delete from duplicate caches to give it chance to be re-submitted
- // this relatively rare operation and implementation is expensive (requires re-hashing)
- handler.deleteFromCaches(tx.rawmsgDataHash, tx.unverifiedTxGroupHash)
-
- }
- return nil
-}
-
func (handler *TxHandler) deleteFromCaches(msgKey *crypto.Digest, canonicalKey *crypto.Digest) {
if handler.cacheConfig.enableFilteringCanonical && canonicalKey != nil {
handler.txCanonicalCache.Delete(canonicalKey)
@@ -529,6 +574,27 @@ func (handler *TxHandler) processIncomingTxn(rawmsg network.IncomingMessage) net
dec := protocol.NewMsgpDecoderBytes(rawmsg.Data)
ntx := 0
consumed := 0
+
+ var err error
+ var capguard *util.ErlCapacityGuard
+ if handler.erl != nil {
+ // consume a capacity unit
+ // if the elastic rate limiter cannot vend a capacity, the error it returns
+ // is sufficient to indicate that we should enable Congestion Control, because
+ // an issue in vending capacity indicates the underlying resource (TXBacklog) is full
+ capguard, err = handler.erl.ConsumeCapacity(rawmsg.Sender.(util.ErlClient))
+ if err != nil {
+ handler.erl.EnableCongestionControl()
+ // if there is no capacity, it is the same as if we failed to put the item onto the backlog, so report such
+ transactionMessagesDroppedFromBacklog.Inc(nil)
+ return network.OutgoingMessage{Action: network.Ignore}
+ }
+ // if the backlog Queue has 50% of its buffer back, turn congestion control off
+ if float64(cap(handler.backlogQueue))*0.5 > float64(len(handler.backlogQueue)) {
+ handler.erl.DisableCongestionControl()
+ }
+ }
+
for {
if len(unverifiedTxGroup) == ntx {
n := make([]transactions.SignedTxn, len(unverifiedTxGroup)*2)
@@ -579,6 +645,7 @@ func (handler *TxHandler) processIncomingTxn(rawmsg network.IncomingMessage) net
unverifiedTxGroup: unverifiedTxGroup,
rawmsgDataHash: msgKey,
unverifiedTxGroupHash: canonicalKey,
+ capguard: capguard,
}:
default:
// if we failed here we want to increase the corresponding metric. It might suggest that we
diff --git a/data/txHandler_test.go b/data/txHandler_test.go
index 4e8dcdfe1..275a8e9e3 100644
--- a/data/txHandler_test.go
+++ b/data/txHandler_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -33,6 +33,8 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/algorand/go-deadlock"
+
"github.com/algorand/go-algorand/agreement"
"github.com/algorand/go-algorand/components/mocks"
"github.com/algorand/go-algorand/config"
@@ -42,7 +44,7 @@ import (
"github.com/algorand/go-algorand/data/pools"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/verify"
- realledger "github.com/algorand/go-algorand/ledger"
+ "github.com/algorand/go-algorand/data/txntest"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/network"
@@ -50,11 +52,17 @@ import (
"github.com/algorand/go-algorand/test/partitiontest"
"github.com/algorand/go-algorand/util/execpool"
"github.com/algorand/go-algorand/util/metrics"
-
- "github.com/algorand/go-deadlock"
)
-func makeTestGenesisAccounts(tb require.TestingT, numUsers int) ([]basics.Address, []*crypto.SignatureSecrets, map[basics.Address]basics.AccountData) {
+// txHandler uses config values to determine backlog size. Tests should use a static value
+var txBacklogSize = 26000
+
+// mock sender is used to implement OnClose, since TXHandlers expect to use Senders and ERL Clients
+type mockSender struct{}
+
+func (m mockSender) OnClose(func()) {}
+
+func makeTestGenesisAccounts(tb testing.TB, numUsers int) ([]basics.Address, []*crypto.SignatureSecrets, map[basics.Address]basics.AccountData) {
addresses := make([]basics.Address, numUsers)
secrets := make([]*crypto.SignatureSecrets, numUsers)
genesis := make(map[basics.Address]basics.AccountData)
@@ -89,14 +97,20 @@ func BenchmarkTxHandlerProcessing(b *testing.B) {
const inMem = true
cfg := config.GetDefaultLocal()
cfg.Archival = true
+ cfg.TxBacklogReservedCapacityPerPeer = 1
+ cfg.IncomingConnectionsLimit = 10
ledger, err := LoadLedger(log, ledgerName, inMem, protocol.ConsensusCurrentVersion, genBal, genesisID, genesisHash, nil, cfg)
require.NoError(b, err)
+ defer ledger.Close()
l := ledger
cfg.TxPoolSize = 75000
cfg.EnableProcessBlockStats = false
- txHandler := makeTestTxHandler(l, cfg)
+ txHandler, err := makeTestTxHandler(l, cfg)
+ require.NoError(b, err)
+ defer txHandler.txVerificationPool.Shutdown()
+ defer close(txHandler.streamVerifierDropped)
makeTxns := func(N int) [][]transactions.SignedTxn {
ret := make([][]transactions.SignedTxn, 0, N)
@@ -141,7 +155,7 @@ func BenchmarkTxHandlerProcessing(b *testing.B) {
b.Logf("verifying %d signedTransactionGroups", len(signedTransactionGroups))
b.ResetTimer()
for i := range signedTransactionGroups {
- verify.TxnGroup(signedTransactionGroups[i], hdr, vtc, l)
+ verify.TxnGroup(signedTransactionGroups[i], &hdr, vtc, l)
}
})
}
@@ -150,8 +164,8 @@ func BenchmarkTxHandlerProcessing(b *testing.B) {
type vtCache struct{}
func (vtCache) Add(txgroup []transactions.SignedTxn, groupCtx *verify.GroupContext) {}
-func (vtCache) AddPayset(txgroup [][]transactions.SignedTxn, groupCtxs []*verify.GroupContext) error {
- return nil
+func (vtCache) AddPayset(txgroup [][]transactions.SignedTxn, groupCtxs []*verify.GroupContext) {
+ return
}
func (vtCache) GetUnverifiedTransactionGroups(payset [][]transactions.SignedTxn, CurrSpecAddrs transactions.SpecialAddresses, CurrProto protocol.ConsensusVersion) [][]transactions.SignedTxn {
return nil
@@ -214,7 +228,7 @@ func TestTxHandlerProcessIncomingTxn(t *testing.T) {
const numTxns = 11
handler := makeTestTxHandlerOrphaned(1)
stxns, blob := makeRandomTransactions(numTxns)
- action := handler.processIncomingTxn(network.IncomingMessage{Data: blob})
+ action := handler.processIncomingTxn(network.IncomingMessage{Data: blob, Sender: mockSender{}})
require.Equal(t, network.OutgoingMessage{Action: network.Ignore}, action)
require.Equal(t, 1, len(handler.backlogQueue))
@@ -432,6 +446,45 @@ func BenchmarkTxHandlerProcessIncomingTxn16(b *testing.B) {
finConsume()
}
+// BenchmarkTxHandlerProcessIncomingLogicTxn16 is similar to BenchmarkTxHandlerProcessIncomingTxn16
+// but with logicsig groups of 4 txns
+func BenchmarkTxHandlerProcessIncomingLogicTxn16(b *testing.B) {
+ deadlockDisable := deadlock.Opts.Disable
+ deadlock.Opts.Disable = true
+ defer func() {
+ deadlock.Opts.Disable = deadlockDisable
+ }()
+
+ const numSendThreads = 16
+ handler := makeTestTxHandlerOrphaned(txBacklogSize)
+
+ // prepare tx groups
+ blobs := make([][]byte, b.N)
+ stxns := make([][]transactions.SignedTxn, b.N)
+ for i := 0; i < b.N; i++ {
+ txns := txntest.CreateTinyManTxGroup(b, true)
+ stxns[i], _ = txntest.CreateTinyManSignedTxGroup(b, txns)
+ var blob []byte
+ for j := range stxns[i] {
+ encoded := protocol.Encode(&stxns[i][j])
+ blob = append(blob, encoded...)
+ }
+ blobs[i] = blob
+ }
+ numTxnsPerGroup := len(stxns[0])
+
+ statsCh := make(chan [4]int, 1)
+ defer close(statsCh)
+ finConsume := benchTxHandlerProcessIncomingTxnConsume(b, handler, numTxnsPerGroup, 0, statsCh)
+
+ // submit tx groups
+ b.ResetTimer()
+ finalizeSubmit := benchTxHandlerProcessIncomingTxnSubmit(b, handler, blobs, numSendThreads)
+
+ finalizeSubmit()
+ finConsume()
+}
+
// BenchmarkTxHandlerIncDeDup checks txn receiving with duplicates
// simulating processing delay
func BenchmarkTxHandlerIncDeDup(b *testing.B) {
@@ -624,7 +677,7 @@ func TestTxHandlerProcessIncomingCensoring(t *testing.T) {
}
t.Run("single", func(t *testing.T) {
- handler := makeTestTxHandlerOrphaned(txBacklogSize)
+ handler := makeTestTxHandlerOrphanedWithContext(context.Background(), txBacklogSize, txBacklogSize, txHandlerConfig{true, true}, 0)
stxns, blob := makeRandomTransactions(1)
stxn := stxns[0]
action := handler.processIncomingTxn(network.IncomingMessage{Data: blob})
@@ -649,7 +702,7 @@ func TestTxHandlerProcessIncomingCensoring(t *testing.T) {
})
t.Run("group", func(t *testing.T) {
- handler := makeTestTxHandlerOrphaned(txBacklogSize)
+ handler := makeTestTxHandlerOrphanedWithContext(context.Background(), txBacklogSize, txBacklogSize, txHandlerConfig{true, true}, 0)
num := rand.Intn(config.MaxTxGroupSize-1) + 2 // 2..config.MaxTxGroupSize
require.LessOrEqual(t, num, config.MaxTxGroupSize)
stxns, blob := makeRandomTransactions(num)
@@ -719,10 +772,10 @@ func TestTxHandlerProcessIncomingCensoring(t *testing.T) {
// makeTestTxHandlerOrphaned creates a tx handler without any backlog consumer.
// It is caller responsibility to run a consumer thread.
func makeTestTxHandlerOrphaned(backlogSize int) *TxHandler {
- return makeTestTxHandlerOrphanedWithContext(context.Background(), txBacklogSize, txBacklogSize, 0)
+ return makeTestTxHandlerOrphanedWithContext(context.Background(), txBacklogSize, txBacklogSize, txHandlerConfig{true, false}, 0)
}
-func makeTestTxHandlerOrphanedWithContext(ctx context.Context, backlogSize int, cacheSize int, refreshInterval time.Duration) *TxHandler {
+func makeTestTxHandlerOrphanedWithContext(ctx context.Context, backlogSize int, cacheSize int, txHandlerConfig txHandlerConfig, refreshInterval time.Duration) *TxHandler {
if backlogSize <= 0 {
backlogSize = txBacklogSize
}
@@ -733,13 +786,13 @@ func makeTestTxHandlerOrphanedWithContext(ctx context.Context, backlogSize int,
backlogQueue: make(chan *txBacklogMsg, backlogSize),
msgCache: makeSaltedCache(cacheSize),
txCanonicalCache: makeDigestCache(cacheSize),
- cacheConfig: txHandlerConfig{true, true},
+ cacheConfig: txHandlerConfig,
}
- handler.msgCache.start(ctx, refreshInterval)
+ handler.msgCache.Start(ctx, refreshInterval)
return handler
}
-func makeTestTxHandler(dl *Ledger, cfg config.Local) *TxHandler {
+func makeTestTxHandler(dl *Ledger, cfg config.Local) (*TxHandler, error) {
tp := pools.MakeTransactionPool(dl.Ledger, cfg, logging.Base())
backlogPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, nil)
opts := TxHandlerOpts{
@@ -839,7 +892,7 @@ func TestTxHandlerProcessIncomingCacheRotation(t *testing.T) {
t.Run("scheduled", func(t *testing.T) {
// double enqueue a single txn message, ensure it discarded
ctx, cancelFunc := context.WithCancel(context.Background())
- handler := makeTestTxHandlerOrphanedWithContext(ctx, txBacklogSize, txBacklogSize, 10*time.Millisecond)
+ handler := makeTestTxHandlerOrphanedWithContext(ctx, txBacklogSize, txBacklogSize, txHandlerConfig{true, true}, 10*time.Millisecond)
var action network.OutgoingMessage
var msg *txBacklogMsg
@@ -900,9 +953,8 @@ func TestTxHandlerProcessIncomingCacheRotation(t *testing.T) {
// TestTxHandlerProcessIncomingCacheBacklogDrop checks if dropped messages are also removed from caches
func TestTxHandlerProcessIncomingCacheBacklogDrop(t *testing.T) {
partitiontest.PartitionTest(t)
- t.Parallel()
- handler := makeTestTxHandlerOrphanedWithContext(context.Background(), 1, 20, 0)
+ handler := makeTestTxHandlerOrphanedWithContext(context.Background(), 1, 20, txHandlerConfig{true, true}, 0)
stxns1, blob1 := makeRandomTransactions(1)
require.Equal(t, 1, len(stxns1))
@@ -931,6 +983,7 @@ func TestTxHandlerProcessIncomingCacheTxPoolDrop(t *testing.T) {
const numUsers = 100
log := logging.TestingLog(t)
+ log.SetLevel(logging.Panic)
// prepare the accounts
addresses, secrets, genesis := makeTestGenesisAccounts(t, numUsers)
@@ -939,13 +992,27 @@ func TestTxHandlerProcessIncomingCacheTxPoolDrop(t *testing.T) {
const inMem = true
cfg := config.GetDefaultLocal()
cfg.Archival = true
+ cfg.EnableTxBacklogRateLimiting = false
cfg.TxIncomingFilteringFlags = 3 // txFilterRawMsg + txFilterCanonical
ledger, err := LoadLedger(log, ledgerName, inMem, protocol.ConsensusCurrentVersion, genBal, genesisID, genesisHash, nil, cfg)
require.NoError(t, err)
+ defer ledger.Close()
l := ledger
- handler := makeTestTxHandler(l, cfg)
- handler.postVerificationQueue = make(chan *txBacklogMsg)
+ handler, err := makeTestTxHandler(l, cfg)
+ require.NoError(t, err)
+ defer handler.txVerificationPool.Shutdown()
+ defer close(handler.streamVerifierDropped)
+
+ // saturate the postVerificationQueue
+loop:
+ for {
+ select {
+ case handler.postVerificationQueue <- &verify.VerificationResult{}:
+ default:
+ break loop
+ }
+ }
makeTxns := func(sendIdx, recvIdx int) ([]transactions.SignedTxn, []byte) {
tx := transactions.Transaction{
@@ -980,8 +1047,22 @@ func TestTxHandlerProcessIncomingCacheTxPoolDrop(t *testing.T) {
require.Equal(t, stxns, msg.unverifiedTxGroup)
initialCount := transactionMessagesDroppedFromPool.GetUint64Value()
- handler.asyncVerifySignature(msg)
- currentCount := transactionMessagesDroppedFromPool.GetUint64Value()
+
+ // emulate handler.Start() without the backlog
+ handler.ctx, handler.ctxCancel = context.WithCancel(context.Background())
+ handler.streamVerifier.Start(handler.ctx)
+ defer handler.streamVerifier.WaitForStop()
+ defer handler.ctxCancel()
+ handler.streamVerifierChan <- &verify.UnverifiedElement{
+ TxnGroup: msg.unverifiedTxGroup, BacklogMessage: msg}
+ var currentCount uint64
+ for x := 0; x < 1000; x++ {
+ currentCount = transactionMessagesDroppedFromPool.GetUint64Value()
+ if currentCount > 0 {
+ break
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
require.Equal(t, initialCount+1, currentCount)
require.Equal(t, 0, handler.msgCache.Len())
require.Equal(t, 0, handler.txCanonicalCache.Len())
@@ -1028,7 +1109,7 @@ func BenchmarkTxHandlerDecoderMsgp(b *testing.B) {
}
// TestTxHandlerIncomingTxHandle checks the correctness with single txns
-func TestTxHandlerIncomingTxHandle(t *testing.T) {
+func TestTxHandlerIncomingTxHandle(t *testing.T) { //nolint:paralleltest // Not parallel because incomingTxHandlerProcessing mutates global metrics
partitiontest.PartitionTest(t)
numberOfTransactionGroups := 1000
@@ -1036,7 +1117,7 @@ func TestTxHandlerIncomingTxHandle(t *testing.T) {
}
// TestTxHandlerIncomingTxGroupHandle checks the correctness with txn groups
-func TestTxHandlerIncomingTxGroupHandle(t *testing.T) {
+func TestTxHandlerIncomingTxGroupHandle(t *testing.T) { //nolint:paralleltest // Not parallel because incomingTxHandlerProcessing mutates global metrics
partitiontest.PartitionTest(t)
numberOfTransactionGroups := 1000 / proto.MaxTxGroupSize
@@ -1044,7 +1125,7 @@ func TestTxHandlerIncomingTxGroupHandle(t *testing.T) {
}
// TestTxHandlerIncomingTxHandleDrops accounts for the dropped txns when the verifier/exec pool is saturated
-func TestTxHandlerIncomingTxHandleDrops(t *testing.T) {
+func TestTxHandlerIncomingTxHandleDrops(t *testing.T) { //nolint:paralleltest // Not parallel because it changes the backlog size
partitiontest.PartitionTest(t)
// use smaller backlog size to test the message drops
@@ -1066,9 +1147,13 @@ func incomingTxHandlerProcessing(maxGroupSize, numberOfTransactionGroups int, t
transactionMessagesDroppedFromBacklog = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromBacklog)
transactionMessagesDroppedFromPool = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromPool)
}()
+ // reset the counters
+ transactionMessagesDroppedFromBacklog = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromBacklog)
+ transactionMessagesDroppedFromPool = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromPool)
const numUsers = 100
log := logging.TestingLog(t)
+ log.SetLevel(logging.Warn)
// prepare the accounts
addresses, secrets, genesis := makeTestGenesisAccounts(t, numUsers)
@@ -1077,24 +1162,31 @@ func incomingTxHandlerProcessing(maxGroupSize, numberOfTransactionGroups int, t
const inMem = true
cfg := config.GetDefaultLocal()
cfg.Archival = true
+ cfg.EnableTxBacklogRateLimiting = false
ledger, err := LoadLedger(log, ledgerName, inMem, protocol.ConsensusCurrentVersion, genBal, genesisID, genesisHash, nil, cfg)
require.NoError(t, err)
+ defer ledger.Close()
- l := ledger
- handler := makeTestTxHandler(l, cfg)
+ handler, err := makeTestTxHandler(ledger, cfg)
+ require.NoError(t, err)
+ defer handler.txVerificationPool.Shutdown()
+ defer close(handler.streamVerifierDropped)
// since Start is not called, set the context here
handler.ctx, handler.ctxCancel = context.WithCancel(context.Background())
defer handler.ctxCancel()
- outChan := make(chan *txBacklogMsg, 10)
+ // emulate handler.Start() without the backlog
+ handler.ctx, handler.ctxCancel = context.WithCancel(context.Background())
+ handler.streamVerifier.Start(handler.ctx)
+
+ testResultChan := make(chan *txBacklogMsg, 10)
wg := sync.WaitGroup{}
wg.Add(1)
// Make a test backlog worker, which is similar to backlogWorker, but sends the results
- // through the outChan instead of passing it to postProcessCheckedTxn
+ // through the testResultChan instead of passing it to postProcessCheckedTxn
go func() {
defer wg.Done()
- defer close(outChan)
for {
// prioritize the postVerificationQueue
select {
@@ -1102,7 +1194,10 @@ func incomingTxHandlerProcessing(maxGroupSize, numberOfTransactionGroups int, t
if !ok {
return
}
- outChan <- wi
+ txBLMsg := wi.BacklogMessage.(*txBacklogMsg)
+ txBLMsg.verificationErr = wi.Err
+ testResultChan <- txBLMsg
+
// restart the loop so that we could empty out the post verification queue.
continue
default:
@@ -1112,29 +1207,20 @@ func incomingTxHandlerProcessing(maxGroupSize, numberOfTransactionGroups int, t
select {
case wi, ok := <-handler.backlogQueue:
if !ok {
- // shut down to end the test
- handler.txVerificationPool.Shutdown()
- close(handler.postVerificationQueue)
- // wait until all the pending responses are obtained.
- // this is not in backlogWorker, maybe should be
- for wi := range handler.postVerificationQueue {
- outChan <- wi
- }
return
}
if handler.checkAlreadyCommitted(wi) {
// this is not expected during the test
continue
}
-
- // enqueue the task to the verification pool.
- handler.txVerificationPool.EnqueueBacklog(handler.ctx, handler.asyncVerifySignature, wi, nil)
-
+ handler.streamVerifierChan <- &verify.UnverifiedElement{TxnGroup: wi.unverifiedTxGroup, BacklogMessage: wi}
case wi, ok := <-handler.postVerificationQueue:
if !ok {
return
}
- outChan <- wi
+ txBLMsg := wi.BacklogMessage.(*txBacklogMsg)
+ txBLMsg.verificationErr = wi.Err
+ testResultChan <- txBLMsg
case <-handler.ctx.Done():
return
@@ -1152,7 +1238,7 @@ func incomingTxHandlerProcessing(maxGroupSize, numberOfTransactionGroups int, t
data = append(data, protocol.Encode(&stxn)...)
}
encodedSignedTransactionGroups =
- append(encodedSignedTransactionGroups, network.IncomingMessage{Data: data})
+ append(encodedSignedTransactionGroups, network.IncomingMessage{Data: data, Sender: mockSender{}})
}
// Process the results and make sure they are correct
@@ -1174,7 +1260,7 @@ func incomingTxHandlerProcessing(maxGroupSize, numberOfTransactionGroups int, t
timer := time.NewTicker(250 * time.Millisecond)
for {
select {
- case wi := <-outChan:
+ case wi := <-testResultChan:
txnCounter = txnCounter + len(wi.unverifiedTxGroup)
groupCounter++
u, _ := binary.Uvarint(wi.unverifiedTxGroup[0].Txn.Note)
@@ -1210,15 +1296,31 @@ func getDropped() (droppedBacklog, droppedPool uint64) {
return
}
-// makeSignedTxnGroups prepares N transaction groups of random (maxGroupSize) sizes with random
-// invalid signatures of a given probability (invalidProb)
-func makeSignedTxnGroups(N, numUsers, maxGroupSize int, invalidProb float32, addresses []basics.Address,
- secrets []*crypto.SignatureSecrets) (ret [][]transactions.SignedTxn,
- badTxnGroups map[uint64]interface{}) {
- badTxnGroups = make(map[uint64]interface{})
+func getTransaction(sender, receiver basics.Address, u int) transactions.Transaction {
+ noteField := make([]byte, binary.MaxVarintLen64)
+ binary.PutUvarint(noteField, uint64(u))
+
+ tx := transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: transactions.Header{
+ Sender: sender,
+ Fee: basics.MicroAlgos{Raw: proto.MinTxnFee * 2},
+ FirstValid: 0,
+ LastValid: basics.Round(proto.MaxTxnLife),
+ GenesisHash: genesisHash,
+ Note: noteField,
+ },
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: receiver,
+ Amount: basics.MicroAlgos{Raw: mockBalancesMinBalance + (rand.Uint64() % 10000)},
+ },
+ }
+ return tx
+}
+func getTransactionGroups(N, numUsers, maxGroupSize int, addresses []basics.Address) [][]transactions.Transaction {
+ txnGrps := make([][]transactions.Transaction, N)
protoMaxGrpSize := proto.MaxTxGroupSize
- ret = make([][]transactions.SignedTxn, 0, N)
for u := 0; u < N; u++ {
grpSize := rand.Intn(protoMaxGrpSize-1) + 1
if grpSize > maxGroupSize {
@@ -1228,72 +1330,189 @@ func makeSignedTxnGroups(N, numUsers, maxGroupSize int, invalidProb float32, add
txns := make([]transactions.Transaction, 0, grpSize)
for g := 0; g < grpSize; g++ {
// generate transactions
- noteField := make([]byte, binary.MaxVarintLen64)
- binary.PutUvarint(noteField, uint64(u))
- tx := transactions.Transaction{
- Type: protocol.PaymentTx,
- Header: transactions.Header{
- Sender: addresses[(u+g)%numUsers],
- Fee: basics.MicroAlgos{Raw: proto.MinTxnFee * 2},
- FirstValid: 0,
- LastValid: basics.Round(proto.MaxTxnLife),
- GenesisHash: genesisHash,
- Note: noteField,
- },
- PaymentTxnFields: transactions.PaymentTxnFields{
- Receiver: addresses[(u+g+1)%numUsers],
- Amount: basics.MicroAlgos{Raw: mockBalancesMinBalance + (rand.Uint64() % 10000)},
- },
- }
+ tx := getTransaction(addresses[(u+g)%numUsers], addresses[(u+g+1)%numUsers], u)
if grpSize > 1 {
txGroup.TxGroupHashes = append(txGroup.TxGroupHashes, crypto.Digest(tx.ID()))
}
txns = append(txns, tx)
}
- groupHash := crypto.HashObj(txGroup)
- signedTxGroup := make([]transactions.SignedTxn, 0, grpSize)
- for g, txn := range txns {
- if grpSize > 1 {
- txn.Group = groupHash
+ if grpSize > 1 {
+ groupHash := crypto.HashObj(txGroup)
+ for t := range txns {
+ txns[t].Group = groupHash
}
- signedTx := txn.Sign(secrets[(u+g)%numUsers])
- signedTx.Txn = txn
+ }
+ txnGrps[u] = txns
+ }
+ return txnGrps
+}
+
+func signTransactionGroups(txnGroups [][]transactions.Transaction, secrets []*crypto.SignatureSecrets, invalidProb float32) (
+ ret [][]transactions.SignedTxn, badTxnGroups map[uint64]interface{}) {
+ numUsers := len(secrets)
+ badTxnGroups = make(map[uint64]interface{})
+ for tg := range txnGroups {
+ grpSize := len(txnGroups[tg])
+ signedTxGroup := make([]transactions.SignedTxn, 0, grpSize)
+ for t := range txnGroups[tg] {
+ signedTx := txnGroups[tg][t].Sign(secrets[(tg+t)%numUsers])
+ signedTx.Txn = txnGroups[tg][t]
signedTxGroup = append(signedTxGroup, signedTx)
}
// randomly make bad signatures
if rand.Float32() < invalidProb {
tinGrp := rand.Intn(grpSize)
signedTxGroup[tinGrp].Sig[0] = signedTxGroup[tinGrp].Sig[0] + 1
- badTxnGroups[uint64(u)] = struct{}{}
+ badTxnGroups[uint64(tg)] = struct{}{}
}
ret = append(ret, signedTxGroup)
}
return
}
+func signMSigTransactionGroups(txnGroups [][]transactions.Transaction, secrets []*crypto.SignatureSecrets,
+ invalidProb float32, msigSize int) (ret [][]transactions.SignedTxn, badTxnGroups map[uint64]interface{}, err error) {
+ ret = make([][]transactions.SignedTxn, len(txnGroups))
+ numUsers := len(secrets)
+ badTxnGroups = make(map[uint64]interface{})
+ badTxnGroupsMU := deadlock.Mutex{}
+ // process them using multiple threads
+ workers := make(chan interface{}, runtime.NumCPU()-1)
+ wg := sync.WaitGroup{}
+ errChan := make(chan error, 1)
+ for tg := range txnGroups {
+ wg.Add(1)
+ workers <- struct{}{}
+ go func(i int) {
+ defer func() {
+ wg.Done()
+ <-workers
+ }()
+ msigVer := uint8(1)
+ msigTHld := uint8(msigSize)
+ pks := make([]crypto.PublicKey, msigSize)
+ for x := 0; x < msigSize; x++ {
+ pks[x] = secrets[(i+x)%numUsers].SignatureVerifier
+ }
+ multiSigAddr, err := crypto.MultisigAddrGen(msigVer, msigTHld, pks)
+ if err != nil {
+ select {
+ case errChan <- err:
+ return
+ default:
+ return
+ }
+ }
+ grpSize := len(txnGroups[i])
+ signedTxGroup := make([]transactions.SignedTxn, grpSize)
+ sigsForTxn := make([]crypto.MultisigSig, msigTHld)
+
+ for t := range txnGroups[i] {
+ txnGroups[i][t].Sender = basics.Address(multiSigAddr)
+ for s := range sigsForTxn {
+ sig, err := crypto.MultisigSign(txnGroups[i][t], crypto.Digest(multiSigAddr), msigVer, msigTHld, pks, *secrets[(i+s)%numUsers])
+ if err != nil {
+ select {
+ case errChan <- err:
+ return
+ default:
+ return
+ }
+ }
+ sigsForTxn[s] = sig
+ }
+ msig, err := crypto.MultisigAssemble(sigsForTxn)
+ if err != nil {
+ select {
+ case errChan <- err:
+ return
+ default:
+ return
+ }
+ }
+ signedTxGroup[t].Txn = txnGroups[i][t]
+ signedTxGroup[t].Msig = msig
+ }
+ // randomly make bad signatures
+ if rand.Float32() < invalidProb {
+ tinGrp := rand.Intn(grpSize)
+ tinMsig := rand.Intn(len(signedTxGroup[tinGrp].Msig.Subsigs))
+ signedTxGroup[tinGrp].Msig.Subsigs[tinMsig].Sig[0] = signedTxGroup[tinGrp].Msig.Subsigs[tinMsig].Sig[0] + 1
+ badTxnGroupsMU.Lock()
+ badTxnGroups[uint64(i)] = struct{}{}
+ badTxnGroupsMU.Unlock()
+ }
+ ret[i] = signedTxGroup
+ }(tg)
+ }
+ wg.Wait()
+ close(errChan)
+ err = <-errChan
+ return
+}
+
+// makeSignedTxnGroups prepares N transaction groups of random (maxGroupSize) sizes with random
+// invalid signatures of a given probability (invalidProb)
+func makeSignedTxnGroups(N, numUsers, maxGroupSize int, invalidProb float32, addresses []basics.Address,
+ secrets []*crypto.SignatureSecrets) (ret [][]transactions.SignedTxn,
+ badTxnGroups map[uint64]interface{}) {
+
+ txnGroups := getTransactionGroups(N, numUsers, maxGroupSize, addresses)
+ ret, badTxnGroups = signTransactionGroups(txnGroups, secrets, invalidProb)
+ return
+}
+
+const numBenchUsers = 512
+
// BenchmarkHandleTxns sends signed transactions directly to the verifier
func BenchmarkHandleTxns(b *testing.B) {
maxGroupSize := 1
- tpss := []int{6000000, 600000, 60000, 6000}
invalidRates := []float32{0.5, 0.001}
- for _, tps := range tpss {
+ for _, ivr := range invalidRates {
+ b.Run(fmt.Sprintf("inv_%.3f", ivr), func(b *testing.B) {
+ txGen := makeSigGenerator(b, numBenchUsers, maxGroupSize, ivr)
+ runHandlerBenchmarkWithBacklog(b, txGen, 0, false)
+ })
+ }
+}
+
+// BenchmarkHandleTxnGroups sends signed transaction groups directly to the verifier
+func BenchmarkHandleTxnGroups(b *testing.B) {
+ maxGroupSize := proto.MaxTxGroupSize / 2
+ invalidRates := []float32{0.5, 0.001}
+ for _, ivr := range invalidRates {
+ b.Run(fmt.Sprintf("inv_%.3f", ivr), func(b *testing.B) {
+ txGen := makeSigGenerator(b, numBenchUsers, maxGroupSize, ivr)
+ runHandlerBenchmarkWithBacklog(b, txGen, 0, false)
+ })
+ }
+}
+
+// BenchmarkHandleMsigTxns sends signed transactions directly to the verifier
+func BenchmarkHandleMsigTxns(b *testing.B) {
+ maxGroupSize := 1
+ msigSizes := []int{64, 16, 8, 4}
+ invalidRates := []float32{0.5, 0.001}
+ for _, msigSize := range msigSizes {
for _, ivr := range invalidRates {
- b.Run(fmt.Sprintf("tps_%d_inv_%.3f", tps, ivr), func(b *testing.B) {
- runHandlerBenchmarkWithBacklog(maxGroupSize, tps, ivr, b, false)
+ b.Run(fmt.Sprintf("msigSize_%d_inv_%.3f", msigSize, ivr), func(b *testing.B) {
+ txGen := makeMsigGenerator(b, numBenchUsers, maxGroupSize, ivr, msigSize)
+ runHandlerBenchmarkWithBacklog(b, txGen, 0, false)
})
}
}
}
// BenchmarkHandleTxnGroups sends signed transaction groups directly to the verifier
-func BenchmarkHandleTxnGroups(b *testing.B) {
+func BenchmarkHandleMsigTxnGroups(b *testing.B) {
maxGroupSize := proto.MaxTxGroupSize / 2
- tpss := []int{6000000, 600000, 60000, 6000}
+ msigSizes := []int{64, 16, 8, 4}
invalidRates := []float32{0.5, 0.001}
- for _, tps := range tpss {
+ for _, msigSize := range msigSizes {
for _, ivr := range invalidRates {
- b.Run(fmt.Sprintf("tps_%d_inv_%.3f", tps, ivr), func(b *testing.B) {
- runHandlerBenchmarkWithBacklog(maxGroupSize, tps, ivr, b, false)
+ b.Run(fmt.Sprintf("msigSize_%d_inv_%.3f", msigSize, ivr), func(b *testing.B) {
+ txGen := makeMsigGenerator(b, numBenchUsers, maxGroupSize, ivr, msigSize)
+ runHandlerBenchmarkWithBacklog(b, txGen, 0, false)
})
}
}
@@ -1308,7 +1527,8 @@ func BenchmarkHandleBLWTxns(b *testing.B) {
for _, tps := range tpss {
for _, ivr := range invalidRates {
b.Run(fmt.Sprintf("tps_%d_inv_%.3f", tps, ivr), func(b *testing.B) {
- runHandlerBenchmarkWithBacklog(maxGroupSize, tps, ivr, b, true)
+ txGen := makeSigGenerator(b, numBenchUsers, maxGroupSize, ivr)
+ runHandlerBenchmarkWithBacklog(b, txGen, tps, true)
})
}
}
@@ -1323,47 +1543,172 @@ func BenchmarkHandleBLWTxnGroups(b *testing.B) {
for _, tps := range tpss {
for _, ivr := range invalidRates {
b.Run(fmt.Sprintf("tps_%d_inv_%.3f", tps, ivr), func(b *testing.B) {
- runHandlerBenchmarkWithBacklog(maxGroupSize, tps, ivr, b, true)
+ txGen := makeSigGenerator(b, numBenchUsers, maxGroupSize, ivr)
+ runHandlerBenchmarkWithBacklog(b, txGen, tps, true)
})
}
}
}
-// runHandlerBenchmarkWithBacklog benchmarks the number of transactions verfied or dropped
-func runHandlerBenchmarkWithBacklog(maxGroupSize, tps int, invalidRate float32, b *testing.B, useBacklogWorker bool) {
+// BenchmarkHandleTxnGroups sends signed transaction groups directly to the verifier
+func BenchmarkHandleLsigTxnGroups(b *testing.B) {
+ maxGroupSize := proto.MaxTxGroupSize / 2
+ invalidRates := []float32{0.5, 0.001}
+ for _, ivr := range invalidRates {
+ b.Run(fmt.Sprintf("lsig-inv_%.3f", ivr), func(b *testing.B) {
+ txGen := makeLsigGenerator(b, numBenchUsers, maxGroupSize, ivr)
+ runHandlerBenchmarkWithBacklog(b, txGen, 0, false)
+ })
+ }
+}
+
+type txGenIf interface {
+ makeLedger(tb testing.TB, cfg config.Local, log logging.Logger, namePrefix string) *Ledger
+ createSignedTxGroups(tb testing.TB, txgCount int) ([][]transactions.SignedTxn, map[uint64]interface{})
+}
+
+type txGenerator struct {
+ numUsers int
+ maxGroupSize int
+ invalidRate float32
+
+ addresses []basics.Address
+ secrets []*crypto.SignatureSecrets
+ genesis map[basics.Address]basics.AccountData
+}
+
+type sigGenerator struct {
+ txGenerator
+}
+
+type msigGenerator struct {
+ txGenerator
+ msigSize int
+}
+
+type lsigGenerator struct {
+ txGenerator
+}
+
+func makeTxGenerator(tb testing.TB, numUsers, maxGroupSize int, invalidRate float32) *txGenerator {
+ addresses, secrets, genesis := makeTestGenesisAccounts(tb, numUsers)
+ return &txGenerator{
+ numUsers: numUsers,
+ maxGroupSize: maxGroupSize,
+ invalidRate: invalidRate,
+ addresses: addresses,
+ secrets: secrets,
+ genesis: genesis,
+ }
+}
+
+func (g *txGenerator) makeLedger(tb testing.TB, cfg config.Local, log logging.Logger, namePrefix string) *Ledger {
+ genBal := bookkeeping.MakeGenesisBalances(g.genesis, sinkAddr, poolAddr)
+ ivrString := strings.IndexAny(fmt.Sprintf("%f", g.invalidRate), "1")
+ ledgerName := fmt.Sprintf("%s-in_mem-w_inv=%d", namePrefix, ivrString)
+ ledgerName = strings.Replace(ledgerName, "#", "-", 1)
+ const inMem = true
+ ledger, err := LoadLedger(log, ledgerName, inMem, protocol.ConsensusCurrentVersion, genBal, genesisID, genesisHash, nil, cfg)
+ require.NoError(tb, err)
+ return ledger
+}
+
+func makeSigGenerator(tb testing.TB, numUsers, maxGroupSize int, invalidRate float32) *sigGenerator {
+ return &sigGenerator{
+ txGenerator: *makeTxGenerator(tb, numUsers, maxGroupSize, invalidRate),
+ }
+}
+
+func (g *sigGenerator) createSignedTxGroups(tb testing.TB, txgCount int) ([][]transactions.SignedTxn, map[uint64]interface{}) {
+ return makeSignedTxnGroups(txgCount, g.numUsers, g.maxGroupSize, g.invalidRate, g.addresses, g.secrets)
+}
+
+func makeMsigGenerator(tb testing.TB, numUsers, maxGroupSize int, invalidRate float32, msigSize int) *msigGenerator {
+ return &msigGenerator{
+ txGenerator: *makeTxGenerator(tb, numUsers, maxGroupSize, invalidRate),
+ msigSize: msigSize,
+ }
+}
+
+func (g *msigGenerator) createSignedTxGroups(tb testing.TB, txgCount int) ([][]transactions.SignedTxn, map[uint64]interface{}) {
+ txnGroups := getTransactionGroups(txgCount, g.numUsers, g.maxGroupSize, g.addresses)
+ signedTransactionGroups, badTxnGroups, err := signMSigTransactionGroups(txnGroups, g.secrets, g.invalidRate, g.msigSize)
+ require.NoError(tb, err)
+ return signedTransactionGroups, badTxnGroups
+}
+
+func makeLsigGenerator(tb testing.TB, numUsers, maxGroupSize int, invalidRate float32) *lsigGenerator {
+ return &lsigGenerator{
+ txGenerator: *makeTxGenerator(tb, numUsers, maxGroupSize, invalidRate),
+ }
+}
+
+func (g *lsigGenerator) createSignedTxGroups(tb testing.TB, txgCount int) ([][]transactions.SignedTxn, map[uint64]interface{}) {
+ stxns := make([][]transactions.SignedTxn, txgCount)
+ badTxnGroups := make(map[uint64]interface{})
+ for i := 0; i < txgCount; i++ {
+ txns := txntest.CreateTinyManTxGroup(tb, true)
+ stxns[i], _ = txntest.CreateTinyManSignedTxGroup(tb, txns)
+
+ // randomly make bad signatures
+ if rand.Float32() < g.invalidRate {
+ tinGrp := rand.Intn(len(txns))
+ if stxns[i][tinGrp].Sig != (crypto.Signature{}) {
+ stxns[i][tinGrp].Sig[0] = stxns[i][tinGrp].Sig[0] + 1
+ } else {
+ stxns[i][tinGrp].Lsig.Logic[0] = 255
+ }
+ badTxnGroups[uint64(i)] = struct{}{}
+ }
+ }
+ return stxns, badTxnGroups
+}
+
+// runHandlerBenchmarkWithBacklog benchmarks the number of transactions verified or dropped
+func runHandlerBenchmarkWithBacklog(b *testing.B, txGen txGenIf, tps int, useBacklogWorker bool) {
defer func() {
// reset the counters
transactionMessagesDroppedFromBacklog = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromBacklog)
transactionMessagesDroppedFromPool = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromPool)
}()
+ // reset the counters
+ transactionMessagesDroppedFromBacklog = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromBacklog)
+ transactionMessagesDroppedFromPool = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromPool)
- const numUsers = 100
log := logging.TestingLog(b)
log.SetLevel(logging.Warn)
- addresses, secrets, genesis := makeTestGenesisAccounts(b, numUsers)
- genBal := bookkeeping.MakeGenesisBalances(genesis, sinkAddr, poolAddr)
- ivrString := strings.IndexAny(fmt.Sprintf("%f", invalidRate), "1")
- ledgerName := fmt.Sprintf("%s-mem-%d-%d", b.Name(), b.N, ivrString)
- ledgerName = strings.Replace(ledgerName, "#", "-", 1)
- const inMem = true
cfg := config.GetDefaultLocal()
cfg.Archival = true
- ledger, err := LoadLedger(log, ledgerName, inMem, protocol.ConsensusCurrentVersion, genBal, genesisID, genesisHash, nil, cfg)
+ cfg.TxBacklogReservedCapacityPerPeer = 1
+ cfg.IncomingConnectionsLimit = 10
+ ledger := txGen.makeLedger(b, cfg, log, fmt.Sprintf("%s-%d", b.Name(), b.N))
+ defer ledger.Close()
+ handler, err := makeTestTxHandler(ledger, cfg)
require.NoError(b, err)
+ defer handler.txVerificationPool.Shutdown()
+ defer close(handler.streamVerifierDropped)
+
+ // The benchmark generates only 1000 txns, and reuses them. This is done for faster benchmark time and the
+ // ability to have long runs without being limited to the memory. The dedup will block the txns once the same
+ // ones are rotated again. If the purpose is to test dedup, then this can be changed by setting
+ // genTCount = b.N
+ handler.cacheConfig.enableFilteringRawMsg = false
+ handler.cacheConfig.enableFilteringCanonical = false
- l := ledger
- handler := makeTestTxHandler(l, cfg)
// since Start is not called, set the context here
handler.ctx, handler.ctxCancel = context.WithCancel(context.Background())
defer handler.ctxCancel()
- testResultChan := handler.postVerificationQueue
+ // emulate handler.Start() without the backlog
+ handler.ctx, handler.ctxCancel = context.WithCancel(context.Background())
+ handler.streamVerifier.Start(handler.ctx)
+
+ testResultChan := make(chan *txBacklogMsg, 10)
wg := sync.WaitGroup{}
if useBacklogWorker {
wg.Add(1)
- testResultChan = make(chan *txBacklogMsg, 10)
// Make a test backlog worker, which is similar to backlogWorker, but sends the results
// through the testResultChan instead of passing it to postProcessCheckedTxn
go func() {
@@ -1375,7 +1720,9 @@ func runHandlerBenchmarkWithBacklog(maxGroupSize, tps int, invalidRate float32,
if !ok {
return
}
- testResultChan <- wi
+ txBLMsg := wi.BacklogMessage.(*txBacklogMsg)
+ txBLMsg.verificationErr = wi.Err
+ testResultChan <- txBLMsg
// restart the loop so that we could empty out the post verification queue.
continue
@@ -1392,13 +1739,14 @@ func runHandlerBenchmarkWithBacklog(maxGroupSize, tps int, invalidRate float32,
// this is not expected during the test
continue
}
- handler.txVerificationPool.EnqueueBacklog(handler.ctx, handler.asyncVerifySignature, wi, nil)
-
+ handler.streamVerifierChan <- &verify.UnverifiedElement{TxnGroup: wi.unverifiedTxGroup, BacklogMessage: wi}
case wi, ok := <-handler.postVerificationQueue:
if !ok {
return
}
- testResultChan <- wi
+ txBLMsg := wi.BacklogMessage.(*txBacklogMsg)
+ txBLMsg.verificationErr = wi.Err
+ testResultChan <- txBLMsg
case <-handler.ctx.Done():
return
@@ -1407,98 +1755,172 @@ func runHandlerBenchmarkWithBacklog(maxGroupSize, tps int, invalidRate float32,
}()
}
- // Prepare the transactions
- signedTransactionGroups, badTxnGroups := makeSignedTxnGroups(b.N, numUsers, maxGroupSize, invalidRate, addresses, secrets)
- var encodedSignedTransactionGroups []network.IncomingMessage
+ // Prepare 1000 transactions
+ genTCount := 1000
+ if b.N < genTCount {
+ genTCount = b.N
+ }
+ signedTransactionGroups, badTxnGroups := txGen.createSignedTxGroups(b, genTCount)
+ var encStxns []network.IncomingMessage
if useBacklogWorker {
- encodedSignedTransactionGroups = make([]network.IncomingMessage, 0, b.N)
+ encStxns = make([]network.IncomingMessage, 0, genTCount)
for _, stxngrp := range signedTransactionGroups {
data := make([]byte, 0)
for _, stxn := range stxngrp {
data = append(data, protocol.Encode(&stxn)...)
}
- encodedSignedTransactionGroups =
- append(encodedSignedTransactionGroups, network.IncomingMessage{Data: data})
+ encStxns = append(encStxns, network.IncomingMessage{Data: data})
}
}
var tt time.Time
// Process the results and make sure they are correct
- rateAdjuster := time.Second / time.Duration(tps)
+ var rateAdjuster time.Duration
+ if tps > 0 {
+ rateAdjuster = time.Second / time.Duration(tps)
+ }
wg.Add(1)
go func() {
defer wg.Done()
groupCounter := uint64(0)
var txnCounter uint64
invalidCounter := 0
+ // report the results
defer func() {
if groupCounter > 1 {
+ timeSinceStart := time.Since(tt)
droppedBacklog, droppedPool := getDropped()
- b.Logf("Input T(grp)PS: %d (delay %f microsec)", tps, float64(rateAdjuster)/float64(time.Microsecond))
- b.Logf("Verified TPS: %d", uint64(txnCounter)*uint64(time.Second)/uint64(time.Since(tt)))
- b.Logf("Time/txn: %d(microsec)", uint64((time.Since(tt)/time.Microsecond))/txnCounter)
+ if tps > 0 {
+ b.Logf("Input T(grp)PS: %d (delay %f microsec)", tps, float64(rateAdjuster)/float64(time.Microsecond))
+ }
+ b.Logf("Verified TPS: %d T(grp)PS: %d", uint64(txnCounter)*uint64(time.Second)/uint64(timeSinceStart),
+ uint64(groupCounter)*uint64(time.Second)/uint64(timeSinceStart))
+ b.Logf("Time/txn: %d(microsec)", uint64(timeSinceStart/time.Microsecond)/txnCounter)
b.Logf("processed total: [%d groups (%d invalid)] [%d txns]", groupCounter, invalidCounter, txnCounter)
b.Logf("dropped: [%d backlog] [%d pool]\n", droppedBacklog, droppedPool)
}
handler.Stop() // cancel the handler ctx
}()
+ counterMutex := deadlock.Mutex{}
stopChan := make(chan interface{})
+ // monitor the counters to tell when everything is processed and the checker should stop
+ wg.Add(1)
go func() {
+ defer wg.Done()
for {
time.Sleep(200 * time.Millisecond)
droppedBacklog, droppedPool := getDropped()
- if int(groupCounter+droppedBacklog+droppedPool) == len(signedTransactionGroups) {
+ counterMutex.Lock()
+ counters := groupCounter + droppedBacklog + droppedPool
+ counterMutex.Unlock()
+ if int(counters) == b.N {
// all the benchmark txns processed
close(stopChan)
return
}
}
}()
-
- for {
- select {
- case wi := <-testResultChan:
- txnCounter = txnCounter + uint64(len(wi.unverifiedTxGroup))
- groupCounter++
- u, _ := binary.Uvarint(wi.unverifiedTxGroup[0].Txn.Note)
- _, inBad := badTxnGroups[u]
- if wi.verificationErr == nil {
- require.False(b, inBad, "No error for invalid signature")
- } else {
- invalidCounter++
- require.True(b, inBad, "Error for good signature")
+ // pick up each output from the verifier and check it is was correctly decided
+ // since the data paths differ, distinguish between useBacklogWorker or not
+ if useBacklogWorker {
+ for {
+ select {
+ case wi := <-testResultChan:
+ txnCounter = txnCounter + uint64(len(wi.unverifiedTxGroup))
+ counterMutex.Lock()
+ groupCounter++
+ counterMutex.Unlock()
+ u, _ := binary.Uvarint(wi.unverifiedTxGroup[0].Txn.Note)
+ _, inBad := badTxnGroups[u]
+ if wi.verificationErr == nil {
+ require.False(b, inBad, "No error for invalid signature")
+ } else {
+ invalidCounter++
+ require.True(b, inBad, "Error for good signature")
+ }
+ if groupCounter == uint64(b.N) {
+ // all the benchmark txns processed
+ return
+ }
+ case <-stopChan:
+ return
}
- if groupCounter == uint64(len(signedTransactionGroups)) {
- // all the benchmark txns processed
+ }
+ } else {
+ for {
+ select {
+ case wi := <-handler.postVerificationQueue:
+ txnCounter = txnCounter + uint64(len(wi.TxnGroup))
+ counterMutex.Lock()
+ groupCounter++
+ counterMutex.Unlock()
+ u, _ := binary.Uvarint(wi.TxnGroup[0].Txn.Note)
+ _, inBad := badTxnGroups[u]
+ if wi.Err == nil {
+ require.False(b, inBad, "No error for invalid signature")
+ } else {
+ invalidCounter++
+ require.True(b, inBad, "Error for good signature")
+ }
+ if groupCounter == uint64(b.N) {
+ // all the benchmark txns processed
+ return
+ }
+ case <-stopChan:
return
}
- case <-stopChan:
- return
}
}
}()
+ completed := false
+ c := 0
+ ticker := &time.Ticker{}
+ if rateAdjuster > 0 {
+ ticker = time.NewTicker(rateAdjuster)
+ }
+ defer ticker.Stop()
b.ResetTimer()
tt = time.Now()
- if useBacklogWorker {
- for _, tg := range encodedSignedTransactionGroups {
- handler.processIncomingTxn(tg)
- time.Sleep(rateAdjuster)
- }
- } else {
- for _, stxngrp := range signedTransactionGroups {
- blm := txBacklogMsg{rawmsg: nil, unverifiedTxGroup: stxngrp}
- handler.txVerificationPool.EnqueueBacklog(handler.ctx, handler.asyncVerifySignature, &blm, nil)
- time.Sleep(rateAdjuster)
+ for !completed {
+ for i := range signedTransactionGroups {
+ if useBacklogWorker {
+ handler.processIncomingTxn(encStxns[i])
+ <-ticker.C
+ } else {
+ stxngrp := signedTransactionGroups[i]
+ blm := txBacklogMsg{rawmsg: nil, unverifiedTxGroup: stxngrp}
+ handler.streamVerifierChan <- &verify.UnverifiedElement{TxnGroup: stxngrp, BacklogMessage: &blm}
+ }
+ c++
+ if c == b.N {
+ completed = true
+ break
+ }
}
}
wg.Wait()
handler.Stop() // cancel the handler ctx
}
-func TestTxHandlerPostProcessError(t *testing.T) {
+func TestTxHandlerPostProcessError(t *testing.T) { //nolint:paralleltest // Not parallel because it mutates global metrics
partitiontest.PartitionTest(t)
- t.Parallel()
+
+ defer func() {
+ transactionMessagesTxnSigVerificationFailed = metrics.MakeCounter(metrics.TransactionMessagesTxnSigVerificationFailed)
+ transactionMessagesAlreadyCommitted = metrics.MakeCounter(metrics.TransactionMessagesAlreadyCommitted)
+ transactionMessagesTxGroupInvalidFee = metrics.MakeCounter(metrics.TransactionMessagesTxGroupInvalidFee)
+ transactionMessagesTxnSigNotWellFormed = metrics.MakeCounter(metrics.TransactionMessagesTxnSigNotWellFormed)
+ transactionMessagesTxnMsigNotWellFormed = metrics.MakeCounter(metrics.TransactionMessagesTxnMsigNotWellFormed)
+ transactionMessagesTxnLogicSig = metrics.MakeCounter(metrics.TransactionMessagesTxnLogicSig)
+ }()
+
+ transactionMessagesTxnSigVerificationFailed = metrics.MakeCounter(metrics.TransactionMessagesTxnSigVerificationFailed)
+ transactionMessagesAlreadyCommitted = metrics.MakeCounter(metrics.TransactionMessagesAlreadyCommitted)
+ transactionMessagesTxGroupInvalidFee = metrics.MakeCounter(metrics.TransactionMessagesTxGroupInvalidFee)
+ transactionMessagesTxnSigNotWellFormed = metrics.MakeCounter(metrics.TransactionMessagesTxnSigNotWellFormed)
+ transactionMessagesTxnMsigNotWellFormed = metrics.MakeCounter(metrics.TransactionMessagesTxnMsigNotWellFormed)
+ transactionMessagesTxnLogicSig = metrics.MakeCounter(metrics.TransactionMessagesTxnLogicSig)
collect := func() map[string]float64 {
// collect all specific error reason metrics except TxGroupErrorReasonNotWellFormed,
@@ -1547,15 +1969,19 @@ func TestTxHandlerPostProcessError(t *testing.T) {
const expected = int(verify.TxGroupErrorReasonNumValues) - 3
require.Len(t, result, expected)
- errVerify := crypto.ErrBatchVerificationFailed
+ errVerify := crypto.ErrBatchHasFailedSigs
txh.postProcessReportErrors(errVerify)
result = collect()
require.Len(t, result, expected+1)
}
-func TestTxHandlerPostProcessErrorWithVerify(t *testing.T) {
+func TestTxHandlerPostProcessErrorWithVerify(t *testing.T) { //nolint:paralleltest // Not parallel because it mutates global metrics
partitiontest.PartitionTest(t)
- t.Parallel()
+
+ defer func() {
+ transactionMessagesTxnNotWellFormed = metrics.MakeCounter(metrics.TransactionMessagesTxnNotWellFormed)
+ }()
+ transactionMessagesTxnNotWellFormed = metrics.MakeCounter(metrics.TransactionMessagesTxnNotWellFormed)
txn := transactions.Transaction{}
stxn := transactions.SignedTxn{Txn: txn}
@@ -1565,7 +1991,7 @@ func TestTxHandlerPostProcessErrorWithVerify(t *testing.T) {
CurrentProtocol: protocol.ConsensusCurrentVersion,
},
}
- _, err := verify.TxnGroup([]transactions.SignedTxn{stxn}, hdr, nil, nil)
+ _, err := verify.TxnGroup([]transactions.SignedTxn{stxn}, &hdr, nil, nil)
var txGroupErr *verify.TxGroupError
require.ErrorAs(t, err, &txGroupErr)
@@ -1580,9 +2006,21 @@ func TestTxHandlerPostProcessErrorWithVerify(t *testing.T) {
}
// TestTxHandlerRememberReportErrors checks Is and As statements work as expected
-func TestTxHandlerRememberReportErrors(t *testing.T) {
+func TestTxHandlerRememberReportErrors(t *testing.T) { //nolint:paralleltest // Not parallel because incomingTxHandlerProcessing mutates global metrics
partitiontest.PartitionTest(t)
- t.Parallel()
+
+ defer func() {
+ transactionMessageTxPoolRememberCounter = metrics.NewTagCounter(
+ "algod_transaction_messages_txpool_remember_err_{TAG}", "Number of transaction messages not remembered by txpool b/c of {TAG}",
+ txPoolRememberTagCap, txPoolRememberPendingEval, txPoolRememberTagNoSpace, txPoolRememberTagFee, txPoolRememberTagTxnDead, txPoolRememberTagTxnEarly, txPoolRememberTagTooLarge, txPoolRememberTagGroupID,
+ txPoolRememberTagTxID, txPoolRememberTagLease, txPoolRememberTagTxIDEval, txPoolRememberTagLeaseEval, txPoolRememberTagEvalGeneric,
+ )
+ }()
+ transactionMessageTxPoolRememberCounter = metrics.NewTagCounter(
+ "algod_transaction_messages_txpool_remember_err_{TAG}", "Number of transaction messages not remembered by txpool b/c of {TAG}",
+ txPoolRememberTagCap, txPoolRememberPendingEval, txPoolRememberTagNoSpace, txPoolRememberTagFee, txPoolRememberTagTxnDead, txPoolRememberTagTxnEarly, txPoolRememberTagTooLarge, txPoolRememberTagGroupID,
+ txPoolRememberTagTxID, txPoolRememberTagLease, txPoolRememberTagTxIDEval, txPoolRememberTagLeaseEval, txPoolRememberTagEvalGeneric,
+ )
var txh TxHandler
result := map[string]float64{}
@@ -1614,25 +2052,57 @@ func TestTxHandlerRememberReportErrors(t *testing.T) {
require.Equal(t, 1, getMetricCounter(txPoolRememberTagFee))
}
+func makeBlockTicker() *blockTicker {
+ return &blockTicker{
+ waiter: make(chan struct{}, 10),
+ }
+}
+
type blockTicker struct {
- cond sync.Cond
+ waiter chan struct{}
}
func (t *blockTicker) OnNewBlock(block bookkeeping.Block, delta ledgercore.StateDelta) {
- t.cond.L.Lock()
- defer t.cond.L.Unlock()
- t.cond.Broadcast()
+ t.waiter <- struct{}{}
}
func (t *blockTicker) Wait() {
- t.cond.L.Lock()
- defer t.cond.L.Unlock()
- t.cond.Wait()
+ timer := time.NewTimer(1 * time.Second)
+ defer timer.Stop()
+ for {
+ select {
+ case <-t.waiter:
+ return
+ case <-timer.C:
+ return
+ }
+ }
}
-func TestTxHandlerRememberReportErrorsWithTxPool(t *testing.T) {
+func TestTxHandlerRememberReportErrorsWithTxPool(t *testing.T) { //nolint:paralleltest // Not parallel because it mutates global metrics
partitiontest.PartitionTest(t)
- t.Parallel()
+ defer func() {
+ transactionMessageTxPoolRememberCounter = metrics.NewTagCounter(
+ "algod_transaction_messages_txpool_remember_err_{TAG}", "Number of transaction messages not remembered by txpool b/c of {TAG}",
+ txPoolRememberTagCap, txPoolRememberPendingEval, txPoolRememberTagNoSpace, txPoolRememberTagFee, txPoolRememberTagTxnDead, txPoolRememberTagTxnEarly, txPoolRememberTagTooLarge, txPoolRememberTagGroupID,
+ txPoolRememberTagTxID, txPoolRememberTagLease, txPoolRememberTagTxIDEval, txPoolRememberTagLeaseEval, txPoolRememberTagEvalGeneric,
+ )
+ transactionMessageTxPoolCheckCounter = metrics.NewTagCounter(
+ "algod_transaction_messages_txpool_check_err_{TAG}", "Number of transaction messages that didn't pass check by txpool b/c of {TAG}",
+ txPoolRememberTagTxnNotWellFormed, txPoolRememberTagTxnDead, txPoolRememberTagTxnEarly, txPoolRememberTagTooLarge, txPoolRememberTagGroupID,
+ txPoolRememberTagTxID, txPoolRememberTagLease, txPoolRememberTagTxIDEval, txPoolRememberTagLeaseEval, txPoolRememberTagEvalGeneric,
+ )
+ }()
+ transactionMessageTxPoolRememberCounter = metrics.NewTagCounter(
+ "algod_transaction_messages_txpool_remember_err_{TAG}", "Number of transaction messages not remembered by txpool b/c of {TAG}",
+ txPoolRememberTagCap, txPoolRememberPendingEval, txPoolRememberTagNoSpace, txPoolRememberTagFee, txPoolRememberTagTxnDead, txPoolRememberTagTxnEarly, txPoolRememberTagTooLarge, txPoolRememberTagGroupID,
+ txPoolRememberTagTxID, txPoolRememberTagLease, txPoolRememberTagTxIDEval, txPoolRememberTagLeaseEval, txPoolRememberTagEvalGeneric,
+ )
+ transactionMessageTxPoolCheckCounter = metrics.NewTagCounter(
+ "algod_transaction_messages_txpool_check_err_{TAG}", "Number of transaction messages that didn't pass check by txpool b/c of {TAG}",
+ txPoolRememberTagTxnNotWellFormed, txPoolRememberTagTxnDead, txPoolRememberTagTxnEarly, txPoolRememberTagTooLarge, txPoolRememberTagGroupID,
+ txPoolRememberTagTxID, txPoolRememberTagLease, txPoolRememberTagTxIDEval, txPoolRememberTagLeaseEval, txPoolRememberTagEvalGeneric,
+ )
result := map[string]float64{}
checkResult := map[string]float64{}
@@ -1683,8 +2153,12 @@ func TestTxHandlerRememberReportErrorsWithTxPool(t *testing.T) {
cfg.TxPoolSize = config.MaxTxGroupSize + 1
ledger, err := LoadLedger(log, ledgerName, inMem, protocol.ConsensusCurrentVersion, genBal, genesisID, genesisHash, nil, cfg)
require.NoError(t, err)
+ defer ledger.Close()
- handler := makeTestTxHandler(ledger, cfg)
+ handler, err := makeTestTxHandler(ledger, cfg)
+ require.NoError(t, err)
+ defer handler.txVerificationPool.Shutdown()
+ defer close(handler.streamVerifierDropped)
// since Start is not called, set the context here
handler.ctx, handler.ctxCancel = context.WithCancel(context.Background())
defer handler.ctxCancel()
@@ -1694,14 +2168,6 @@ func TestTxHandlerRememberReportErrorsWithTxPool(t *testing.T) {
handler.postProcessCheckedTxn(&wi)
require.Equal(t, 1, getMetricCounter(txPoolRememberTagTxnDead))
- // trigger max pool capacity metric
- hdr := bookkeeping.BlockHeader{
- Round: 1,
- UpgradeState: bookkeeping.UpgradeState{
- CurrentProtocol: protocol.ConsensusCurrentVersion,
- },
- }
-
txn1 := transactions.Transaction{
Type: protocol.PaymentTx,
Header: transactions.Header{
@@ -1813,30 +2279,199 @@ func TestTxHandlerRememberReportErrorsWithTxPool(t *testing.T) {
// require.Equal(t, 1, getMetricCounter(txPoolRememberFee))
// make an invalid block to fail recompute pool and expose transactionMessageTxGroupRememberNoPendingEval metric
- blockTicker := &blockTicker{cond: *sync.NewCond(&deadlock.Mutex{})}
- blockListeners := []realledger.BlockListener{
+ blockTicker := makeBlockTicker()
+ blockListeners := []ledgercore.BlockListener{
handler.txPool,
blockTicker,
}
ledger.RegisterBlockListeners(blockListeners)
- hdr = bookkeeping.BlockHeader{
- Round: 1,
- UpgradeState: bookkeeping.UpgradeState{
- CurrentProtocol: "test",
- },
- }
+ // add few blocks: on ci sometimes blockTicker is not fired in time in case of a single block
+ for i := basics.Round(1); i <= 3; i++ {
+ hdr := bookkeeping.BlockHeader{
+ Round: i,
+ UpgradeState: bookkeeping.UpgradeState{
+ CurrentProtocol: "test",
+ },
+ }
- blk := bookkeeping.Block{
- BlockHeader: hdr,
- Payset: []transactions.SignedTxnInBlock{{}},
+ blk := bookkeeping.Block{
+ BlockHeader: hdr,
+ Payset: []transactions.SignedTxnInBlock{{}},
+ }
+ vb := ledgercore.MakeValidatedBlock(blk, ledgercore.StateDelta{})
+ err = ledger.AddValidatedBlock(vb, agreement.Certificate{})
+ require.NoError(t, err)
}
- vb := ledgercore.MakeValidatedBlock(blk, ledgercore.StateDelta{})
- err = ledger.AddValidatedBlock(vb, agreement.Certificate{})
- require.NoError(t, err)
blockTicker.Wait()
wi.unverifiedTxGroup = []transactions.SignedTxn{}
handler.postProcessCheckedTxn(&wi)
require.Equal(t, 1, getMetricCounter(txPoolRememberPendingEval))
}
+
+func TestMakeTxHandlerErrors(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ opts := TxHandlerOpts{
+ nil, nil, nil, &mocks.MockNetwork{}, "", crypto.Digest{}, config.Local{},
+ }
+ _, err := MakeTxHandler(opts)
+ require.Error(t, err, ErrInvalidTxPool)
+
+ opts = TxHandlerOpts{
+ &pools.TransactionPool{}, nil, nil, &mocks.MockNetwork{}, "", crypto.Digest{}, config.Local{},
+ }
+ _, err = MakeTxHandler(opts)
+ require.Error(t, err, ErrInvalidLedger)
+
+ // it is not possible to test MakeStreamVerifier returning an error, because it is not possible to
+ // get the leger return an error for returining the header of its latest round
+}
+
+// TestTxHandlerRestartWithBacklogAndTxPool starts txHandler, sends transactions,
+// stops, starts in a loop, sends more transactions, and makes sure all the transactions
+// are accounted for. It uses the production backlog worker
+func TestTxHandlerRestartWithBacklogAndTxPool(t *testing.T) { //nolint:paralleltest // Not parallel because it mutates global metrics
+ partitiontest.PartitionTest(t)
+ transactionMessagesDroppedFromBacklog = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromBacklog)
+ transactionMessagesDroppedFromPool = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromPool)
+ transactionMessagesTxnSigVerificationFailed = metrics.MakeCounter(metrics.TransactionMessagesTxnSigVerificationFailed)
+ transactionMessagesBacklogErr = metrics.MakeCounter(metrics.TransactionMessagesBacklogErr)
+ transactionMessagesAlreadyCommitted = metrics.MakeCounter(metrics.TransactionMessagesAlreadyCommitted)
+ transactionMessagesRemember = metrics.MakeCounter(metrics.TransactionMessagesRemember)
+ transactionMessagesHandled = metrics.MakeCounter(metrics.TransactionMessagesHandled)
+
+ defer func() {
+ // reset the counters
+ transactionMessagesDroppedFromBacklog = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromBacklog)
+ transactionMessagesDroppedFromPool = metrics.MakeCounter(metrics.TransactionMessagesDroppedFromPool)
+ transactionMessagesTxnSigVerificationFailed = metrics.MakeCounter(metrics.TransactionMessagesTxnSigVerificationFailed)
+ transactionMessagesBacklogErr = metrics.MakeCounter(metrics.TransactionMessagesBacklogErr)
+ transactionMessagesAlreadyCommitted = metrics.MakeCounter(metrics.TransactionMessagesAlreadyCommitted)
+ transactionMessagesRemember = metrics.MakeCounter(metrics.TransactionMessagesRemember)
+ transactionMessagesHandled = metrics.MakeCounter(metrics.TransactionMessagesHandled)
+ }()
+
+ const numUsers = 100
+ log := logging.TestingLog(t)
+ log.SetLevel(logging.Warn)
+ addresses := make([]basics.Address, numUsers)
+ secrets := make([]*crypto.SignatureSecrets, numUsers)
+
+ // avoid printing the warning messages
+ origLevel := logging.Base().GetLevel()
+ defer func() { logging.Base().SetLevel(origLevel) }()
+ logging.Base().SetLevel(logging.Error)
+
+ // prepare the accounts
+ genesis := make(map[basics.Address]basics.AccountData)
+ for i := 0; i < numUsers; i++ {
+ secret := keypair()
+ addr := basics.Address(secret.SignatureVerifier)
+ secrets[i] = secret
+ addresses[i] = addr
+ genesis[addr] = basics.AccountData{
+ Status: basics.Online,
+ MicroAlgos: basics.MicroAlgos{Raw: 10000000000000},
+ }
+ }
+ genesis[poolAddr] = basics.AccountData{
+ Status: basics.NotParticipating,
+ MicroAlgos: basics.MicroAlgos{Raw: config.Consensus[protocol.ConsensusCurrentVersion].MinBalance},
+ }
+
+ // setup the ledger
+ require.Equal(t, len(genesis), numUsers+1)
+ genBal := bookkeeping.MakeGenesisBalances(genesis, sinkAddr, poolAddr)
+ ledgerName := fmt.Sprintf("%s-mem", t.Name())
+ const inMem = true
+ cfg := config.GetDefaultLocal()
+ cfg.Archival = true
+ ledger, err := LoadLedger(log, ledgerName, inMem, protocol.ConsensusCurrentVersion, genBal, genesisID, genesisHash, nil, cfg)
+ require.NoError(t, err)
+ defer ledger.Ledger.Close()
+
+ handler, err := makeTestTxHandler(ledger, cfg)
+ require.NoError(t, err)
+ defer handler.txVerificationPool.Shutdown()
+ defer close(handler.streamVerifierDropped)
+ // prepare the transactions
+ numTxns := 3000
+ maxGroupSize := 1
+ tps := 40000
+ invalidRate := float32(0.5)
+ rateAdjuster := time.Second / time.Duration(tps)
+ signedTransactionGroups, badTxnGroups := makeSignedTxnGroups(numTxns, numUsers, maxGroupSize, invalidRate, addresses, secrets)
+ var encodedSignedTransactionGroups []network.IncomingMessage
+
+ encodedSignedTransactionGroups = make([]network.IncomingMessage, 0, numTxns)
+ for _, stxngrp := range signedTransactionGroups {
+ data := make([]byte, 0)
+ for _, stxn := range stxngrp {
+ data = append(data, protocol.Encode(&stxn)...)
+ }
+ encodedSignedTransactionGroups =
+ append(encodedSignedTransactionGroups, network.IncomingMessage{Data: data})
+ }
+
+ // start the handler
+ handler.Start()
+
+ // send the transactions to the backlog worker
+ for _, tg := range encodedSignedTransactionGroups[0 : numTxns/2] {
+ handler.processIncomingTxn(tg)
+ time.Sleep(rateAdjuster)
+ }
+ // stop in a loop to test for possible race conditions
+ for x := 0; x < 1000; x++ {
+ handler.Stop()
+ handler.Start()
+ }
+ handler.Stop()
+
+ // send the second half after stopping the txHandler
+ for _, tg := range encodedSignedTransactionGroups[numTxns/2:] {
+ handler.processIncomingTxn(tg)
+ time.Sleep(rateAdjuster)
+ }
+
+ // check that all the incomming transactions are accounted for
+ droppeda, droppedb := getDropped()
+ dropped := droppeda + droppedb
+ stuckInBLQueue := uint64(len(handler.backlogQueue))
+ resultBadTxnCount := transactionMessagesTxnSigVerificationFailed.GetUint64Value()
+ resultGoodTxnCount := transactionMessagesHandled.GetUint64Value()
+ shutdownDropCount := transactionMessagesBacklogErr.GetUint64Value()
+ require.Equal(t, numTxns, int(dropped+resultGoodTxnCount+resultBadTxnCount+stuckInBLQueue+shutdownDropCount))
+
+ // start the handler again
+ handler.Start()
+ defer handler.Stop()
+
+ // no dpulicates are sent at this point
+ require.Equal(t, 0, int(transactionMessagesAlreadyCommitted.GetUint64Value()))
+
+ // send the same set of transactions again
+ for _, tg := range encodedSignedTransactionGroups {
+ handler.processIncomingTxn(tg)
+ time.Sleep(rateAdjuster)
+ }
+
+ inputGoodTxnCount := len(signedTransactionGroups) - len(badTxnGroups)
+ tp := handler.txPool
+ // Wait untill all the expected transactions are in the pool
+ for x := 0; x < 100; x++ {
+ if len(tp.PendingTxGroups()) == inputGoodTxnCount {
+ break
+ }
+ time.Sleep(40 * time.Millisecond)
+ }
+
+ // check the couters and the accepted transactions
+ require.Equal(t, inputGoodTxnCount, len(tp.PendingTxGroups()))
+ for _, txg := range tp.PendingTxGroups() {
+ u, _ := binary.Uvarint(txg[0].Txn.Note)
+ _, inBad := badTxnGroups[u]
+ require.False(t, inBad, "invalid transaction accepted")
+ }
+}
diff --git a/data/txntest/defi.go b/data/txntest/defi.go
index 888b51c0e..635393a67 100644
--- a/data/txntest/defi.go
+++ b/data/txntest/defi.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -526,7 +526,7 @@ func CreateTinyManSignedTxGroup(tb testing.TB, txns []Txn) ([]transactions.Signe
ops, err := logic.AssembleString(TmLsig)
require.NoError(tb, err)
- stxns := SignedTxns(&txns[0], &txns[1], &txns[2], &txns[3])
+ stxns := Group(&txns[0], &txns[1], &txns[2], &txns[3])
stxns[1].Lsig.Logic = ops.Program
stxns[3].Lsig.Logic = ops.Program
diff --git a/data/txntest/txn.go b/data/txntest/txn.go
index f1ac3768b..8e9f699f8 100644
--- a/data/txntest/txn.go
+++ b/data/txntest/txn.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -287,10 +287,10 @@ func (tx Txn) SignedTxnWithAD() transactions.SignedTxnWithAD {
return transactions.SignedTxnWithAD{SignedTxn: tx.SignedTxn()}
}
-// SignedTxns turns a list of Txns into a slice of SignedTxns with
-// GroupIDs set properly to make them a transaction group. Maybe
-// another name is more approrpriate
-func SignedTxns(txns ...*Txn) []transactions.SignedTxn {
+// Group turns a list of Txns into a slice of SignedTxns with
+// GroupIDs set properly to make them a transaction group. The input
+// Txns are modified with the calculated GroupID.
+func Group(txns ...*Txn) []transactions.SignedTxn {
txgroup := transactions.TxGroup{
TxGroupHashes: make([]crypto.Digest, len(txns)),
}
diff --git a/docker/README.md b/docker/README.md
index c40a21ed2..78099e602 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -1,13 +1,14 @@
# Algod Container
-General purpose algod docker container.
+[![DockerHub](https://img.shields.io/badge/DockerHub-blue)](https://hub.docker.com/r/algorand/algod)
+General purpose algod container image.
-# Image Configuration
+## Image Configuration
There are a number of special files and environment variables used to control how a container is started.
-## Default Configuration
+### Default Configuration
By default the following config.json overrides are applied:
@@ -20,7 +21,7 @@ By default the following config.json overrides are applied:
| IsIndexerActive | false |
| EnableDeveloperAPI | true |
-## Environment Variables
+### Environment Variables
The following environment variables can be supplied. Except when noted, it is possible to reconfigure deployments even after the data directory has been initialized.
@@ -34,23 +35,24 @@ The following environment variables can be supplied. Except when noted, it is po
| TOKEN | If set, overrides the REST API token. |
| ADMIN_TOKEN | If set, overrides the REST API admin token. |
+### Special Files
-## Special Files
-
-Configuration can be modified by specifying certian files. These can be changed each time you start the container if the data directory is a mounted volume.
+Configuration can be modified by specifying certain files. These can be changed each time you start the container if the data directory is a mounted volume.
| File | Description |
| ---- | ----------- |
-| /etc/config.json | Override default configurations by providing your own file. |
-| /etc/algod.token | Override default randomized REST API token. |
-| /etc/algod.admin.token | Override default randomized REST API admin token. |
+| /etc/algorand/config.json | Override default configurations by providing your own file. |
+| /etc/algorand/algod.token | Override default randomized REST API token. |
+| /etc/algorand/algod.admin.token | Override default randomized REST API admin token. |
+| /etc/algorand/logging.config | Use a custom [logging.config](https://developer.algorand.org/docs/run-a-node/reference/telemetry-config/#configuration) file for configuring telemetry. |
-TODO: `/etc/template.json` for overriding the private network topology.
+TODO: `/etc/algorand/template.json` for overriding the private network topology.
-# Example Configuration
+## Example Configuration
The following command launches a container configured with one of the public networks:
-```
+
+```bash
docker run --rm -it \
-p 4190:8080 \
-e NETWORK=mainnet \
@@ -63,21 +65,51 @@ docker run --rm -it \
```
Explanation of parts:
+
* `-p 4190:8080` maps the internal algod REST API to local port 4190
* `-e NETWORK=` can be set to any of the supported public networks.
* `-e FAST_CATCHUP=` causes fast catchup to start shortly after launching the network.
-* `-e TELEMETRY_NAME=` enables telemetry reporting to Algorand for network health analysis.
+* `-e TELEMETRY_NAME=` enables telemetry reporting to Algorand for network health analysis. The value of this variable takes precedence over the `name` attribute set in `/etc/algorand/logging.config`.
* `-e TOKEN=` sets the REST API token to use.
-* `-v ${PWD}/data:/algod/data/` mounts a local volume to the data directory, which can be used to restart and upgrad the deployment.
-
+* `-v ${PWD}/data:/algod/data/` mounts a local volume to the data directory, which can be used to restart and upgrade the deployment.
-# Mounting the Data Directory
+## Mounting the Data Directory
The data directory located at `/algod/data`. Mounting a volume at that location will allow you to shutdown and resume the node.
-## Private Network
+### Volume Permissions
+
+The container executes in the context of the `algorand` user with it's own UID and GID which is handled differently depending on your operating system. Here are a few options for how to work with this environment:
+
+#### Named Volume
+
+Using a named volume will work without any specific configuration in most cases:
+
+```bash
+docker volume create algod-data
+docker run -it --rm -d -v algod-data:/algod/data algorand/algod
+```
+
+#### Local Directory without SELinux
+
+Explicitly set the UID and GID of the container:
+
+```bash
+docker run -it --rm -d -v /srv/data:/algod/data -u $UID:$GID algorand/algod
+```
+
+#### Local Directory with SELinux
+
+Set the UID and GID of the container while add the `Z` option to the volume definition:
+
+```bash
+docker run -it --rm -d -v /srv/data:/algod/data:Z -u $UID:$GID algorand/algod
+```
+
+> See the documentation on [configuring the selinux label](https://docs.docker.com/storage/bind-mounts/#configure-the-selinux-label).
+
+### Private Network
Private networks work a little bit differently. They are configured with, potentially, several data directories. The default topology supplied with this container is installed to `/algod/`, and has a single node named `data`. This means the private network has a data directory at `/algod/data`, matching the production configuration.
Because the root directory contains some metadata, if persistence of the private network is required, you should mount the volume `/algod/` instead of `/algod/data`. This will ensure the extra metadata is included when changing images.
-
diff --git a/docker/files/build/install.sh b/docker/files/build/install.sh
index 20d5766e4..abc57cc5e 100755
--- a/docker/files/build/install.sh
+++ b/docker/files/build/install.sh
@@ -82,7 +82,11 @@ fi
git log -n 5
./scripts/configure_dev.sh
-make build
-./scripts/dev_install.sh -p "${BINDIR}" -d "${ALGORAND_DATA}"
+# make sure the makefile uses specific values for BUILD_NUMBER and BRANCH
+BUILD_NUMBER="" BRANCH="$BRANCH" make build
+
+shopt -s extglob
+
+cd "$BINDIR" && rm -vrf !(algocfg|algod|algokey|diagcfg|goal|kmd|msgpacktool|node_exporter|tealdbg|update.sh|updater|COPYING)
"$BINDIR"/algod -v
diff --git a/docker/files/run/run.sh b/docker/files/run/run.sh
index 027c0cc7f..627665dd6 100755
--- a/docker/files/run/run.sh
+++ b/docker/files/run/run.sh
@@ -13,22 +13,25 @@ function apply_configuration() {
cd "$ALGORAND_DATA"
# check for config file overrides.
- if [ -f "/etc/config.json" ]; then
- cp /etc/config.json config.json
+ if [ -f "/etc/algorand/config.json" ]; then
+ cp /etc/algorand/config.json config.json
fi
- if [ -f "/etc/algod.token" ]; then
- cp /etc/algod.token algod.token
+ if [ -f "/etc/algorand/algod.token" ]; then
+ cp /etc/algorand/algod.token algod.token
fi
- if [ -f "/etc/algod.admin.token" ]; then
- cp /etc/algod.admin.token algod.admin.token
+ if [ -f "/etc/algorand/algod.admin.token" ]; then
+ cp /etc/algorand/algod.admin.token algod.admin.token
+ fi
+ if [ -f "/etc/algorand/logging.config" ]; then
+ cp /etc/algorand/logging.config logging.config
fi
# check for environment variable overrides.
if [ "$TOKEN" != "" ]; then
- echo "$TOKEN" > algod.token
+ echo "$TOKEN" >algod.token
fi
if [ "$ADMIN_TOKEN" != "" ]; then
- echo "$ADMIN_TOKEN" > algod.admin.token
+ echo "$ADMIN_TOKEN" >algod.admin.token
fi
# configure telemetry
@@ -57,8 +60,8 @@ function start_public_network() {
apply_configuration
- if [ $FAST_CATCHUP ]; then
- catchup&
+ if [[ $FAST_CATCHUP ]]; then
+ catchup &
fi
# redirect output to stdout
algod -o
@@ -75,20 +78,19 @@ function configure_data_dir() {
}
function start_new_public_network() {
- cd /node
- if [ ! -d "run/genesis/$NETWORK" ]; then
+ cd /algod
+ if [ ! -d "/node/run/genesis/${NETWORK}" ]; then
echo "No genesis file for '$NETWORK' is available."
exit 1
fi
mkdir -p "$ALGORAND_DATA"
- mv dataTemplate/* "$ALGORAND_DATA"
- rm -rf dataTemplate
- cp "run/genesis/$NETWORK/genesis.json" "$ALGORAND_DATA/genesis.json"
cd "$ALGORAND_DATA"
- mv config.json.example config.json
+ cp "/node/run/genesis/${NETWORK}/genesis.json" genesis.json
+ cp /node/run/config.json.example config.json
+
configure_data_dir
local ID
@@ -109,18 +111,17 @@ function start_private_network() {
apply_configuration
# TODO: Is there a way to properly exec a private network?
- goal network start -r "$ALGORAND_DATA/.."
- tail -f "$ALGORAND_DATA/node.log"
+ goal network start -r "${ALGORAND_DATA}/.."
+ tail -f "${ALGORAND_DATA}/node.log"
}
function start_new_private_network() {
- cd /node
local TEMPLATE="template.json"
if [ "$DEV_MODE" ]; then
TEMPLATE="devmode_template.json"
fi
- sed -i "s/NUM_ROUNDS/${NUM_ROUNDS:-30000}/" "run/$TEMPLATE"
- goal network create -n dockernet -r "$ALGORAND_DATA/.." -t "run/$TEMPLATE"
+ sed -i "s/NUM_ROUNDS/${NUM_ROUNDS:-30000}/" "/node/run/$TEMPLATE"
+ goal network create --noclean -n dockernet -r "${ALGORAND_DATA}/.." -t "/node/run/$TEMPLATE"
configure_data_dir
start_private_network
}
diff --git a/gen/generate.go b/gen/generate.go
index 9274da420..ea8792118 100644
--- a/gen/generate.go
+++ b/gen/generate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/gen/generate_test.go b/gen/generate_test.go
index 64c393b5b..e154e7c44 100644
--- a/gen/generate_test.go
+++ b/gen/generate_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/gen/walletData.go b/gen/walletData.go
index 7790e9a6b..9823c03b5 100644
--- a/gen/walletData.go
+++ b/gen/walletData.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/go.mod b/go.mod
index d60536758..a1f20d7df 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.17
require (
github.com/DataDog/zstd v1.5.2
- github.com/algorand/avm-abi v0.1.1
+ github.com/algorand/avm-abi v0.2.0
github.com/algorand/falcon v0.0.0-20220727072124-02a2a64c4414
github.com/algorand/go-codec/codec v1.1.8
github.com/algorand/go-deadlock v0.2.2
diff --git a/go.sum b/go.sum
index 300b5f8a1..68a08e084 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,8 @@
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
-github.com/algorand/avm-abi v0.1.0 h1:znZFQXpSUVYz37vXbaH5OZG2VK4snTyXwnc/tV9CVr4=
-github.com/algorand/avm-abi v0.1.0/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g=
-github.com/algorand/avm-abi v0.1.1 h1:dbyQKzXiyaEbzpmqXFB30yAhyqseBsyqXTyZbNbkh2Y=
-github.com/algorand/avm-abi v0.1.1/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g=
+github.com/algorand/avm-abi v0.2.0 h1:bkjsG+BOEcxUcnGSALLosmltE0JZdg+ZisXKx0UDX2k=
+github.com/algorand/avm-abi v0.2.0/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g=
github.com/algorand/falcon v0.0.0-20220727072124-02a2a64c4414 h1:nwYN+GQ7Z5OOfZwqBO1ma7DSlP7S1YrKWICOyjkwqrc=
github.com/algorand/falcon v0.0.0-20220727072124-02a2a64c4414/go.mod h1:OkQyHlGvS0kLNcIWbC21/uQcnbfwSOQm+wiqWwBG9pQ=
github.com/algorand/go-codec v1.1.8/go.mod h1:XhzVs6VVyWMLu6cApb9/192gBjGRVGm5cX5j203Heg4=
diff --git a/installer/config.json.example b/installer/config.json.example
index 900238808..137578e0f 100644
--- a/installer/config.json.example
+++ b/installer/config.json.example
@@ -1,15 +1,16 @@
{
- "Version": 26,
+ "Version": 27,
"AccountUpdatesStatsInterval": 5000000000,
"AccountsRebuildSynchronousMode": 1,
- "AgreementIncomingBundlesQueueLength": 7,
- "AgreementIncomingProposalsQueueLength": 25,
- "AgreementIncomingVotesQueueLength": 10000,
+ "AgreementIncomingBundlesQueueLength": 15,
+ "AgreementIncomingProposalsQueueLength": 50,
+ "AgreementIncomingVotesQueueLength": 20000,
"AnnounceParticipationKey": true,
"Archival": false,
"BaseLoggerDebugLevel": 4,
"BlockServiceCustomFallbackEndpoints": "",
"BroadcastConnectionsLimit": -1,
+ "CadaverDirectory": "",
"CadaverSizeTarget": 0,
"CatchpointFileHistoryLength": 365,
"CatchpointInterval": 10000,
@@ -38,6 +39,7 @@
"EnableBlockServiceFallbackToArchiver": true,
"EnableCatchupFromArchiveServers": false,
"EnableDeveloperAPI": false,
+ "EnableExperimentalAPI": false,
"EnableGossipBlockService": true,
"EnableIncomingMessageFilter": false,
"EnableLedgerService": false,
@@ -49,6 +51,7 @@
"EnableRequestLogger": false,
"EnableRuntimeMetrics": false,
"EnableTopAccountsReporting": false,
+ "EnableTxBacklogRateLimiting": false,
"EnableUsageLog": false,
"EnableVerbosedTransactionSyncLogging": false,
"EndpointAddress": "127.0.0.1:0",
@@ -56,7 +59,8 @@
"ForceFetchTransactions": false,
"ForceRelayMessages": false,
"GossipFanout": 4,
- "IncomingConnectionsLimit": 800,
+ "HeartbeatUpdateInterval": 600,
+ "IncomingConnectionsLimit": 2400,
"IncomingMessageFilterBucketCount": 5,
"IncomingMessageFilterBucketSize": 512,
"IsIndexerActive": false,
@@ -68,7 +72,7 @@
"MaxAPIResourcesPerAccount": 100000,
"MaxAcctLookback": 4,
"MaxCatchpointDownloadDuration": 7200000000000,
- "MaxConnectionsPerIP": 30,
+ "MaxConnectionsPerIP": 15,
"MinCatchpointFileDownloadBytesPerSecond": 20480,
"NetAddress": "",
"NetworkMessageTraceServer": "",
@@ -98,6 +102,9 @@
"TelemetryToLog": true,
"TransactionSyncDataExchangeRate": 0,
"TransactionSyncSignificantMessageThreshold": 0,
+ "TxBacklogReservedCapacityPerPeer": 20,
+ "TxBacklogServiceRateWindowSeconds": 10,
+ "TxBacklogSize": 26000,
"TxIncomingFilteringFlags": 1,
"TxPoolExponentialIncreaseFactor": 2,
"TxPoolSize": 75000,
diff --git a/ledger/accountdb.go b/ledger/acctdeltas.go
index c82a4e5ee..2bbb5e5e1 100644
--- a/ledger/accountdb.go
+++ b/ledger/acctdeltas.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -20,14 +20,11 @@ import (
"bytes"
"context"
"database/sql"
- "errors"
"fmt"
- "math"
-
- "github.com/algorand/msgp/msgp"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/ledger/encoded"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store"
"github.com/algorand/go-algorand/protocol"
@@ -103,7 +100,7 @@ const MaxEncodedBaseAccountDataSize = 350
const MaxEncodedBaseResourceDataSize = 20000
// prepareNormalizedBalancesV5 converts an array of encodedBalanceRecordV5 into an equal size array of normalizedAccountBalances.
-func prepareNormalizedBalancesV5(bals []encodedBalanceRecordV5, proto config.ConsensusParams) (normalizedAccountBalances []store.NormalizedAccountBalance, err error) {
+func prepareNormalizedBalancesV5(bals []encoded.BalanceRecordV5, proto config.ConsensusParams) (normalizedAccountBalances []store.NormalizedAccountBalance, err error) {
normalizedAccountBalances = make([]store.NormalizedAccountBalance, len(bals))
for i, balance := range bals {
normalizedAccountBalances[i].Address = balance.Address
@@ -141,8 +138,8 @@ func prepareNormalizedBalancesV5(bals []encodedBalanceRecordV5, proto config.Con
return
}
-// prepareNormalizedBalancesV6 converts an array of encodedBalanceRecordV6 into an equal size array of normalizedAccountBalances.
-func prepareNormalizedBalancesV6(bals []encodedBalanceRecordV6, proto config.ConsensusParams) (normalizedAccountBalances []store.NormalizedAccountBalance, err error) {
+// prepareNormalizedBalancesV6 converts an array of encoded.BalanceRecordV6 into an equal size array of normalizedAccountBalances.
+func prepareNormalizedBalancesV6(bals []encoded.BalanceRecordV6, proto config.ConsensusParams) (normalizedAccountBalances []store.NormalizedAccountBalance, err error) {
normalizedAccountBalances = make([]store.NormalizedAccountBalance, len(bals))
for i, balance := range bals {
normalizedAccountBalances[i].Address = balance.Address
@@ -488,7 +485,8 @@ func (a *compactAccountDeltas) accountsLoadOld(tx *sql.Tx) (err error) {
case sql.ErrNoRows:
// we don't have that account, just return an empty record.
a.updateOld(idx, store.PersistedAccountData{Addr: addr})
- err = nil
+ // Note: the err will be ignored in this case since `err` is being shadowed.
+ // this behaviour is equivalent to `err = nil`
default:
// unexpected error - let the caller know that we couldn't complete the operation.
return err
@@ -1062,601 +1060,3 @@ func onlineAccountsNewRoundImpl(
return
}
-
-// catchpointAccountResourceCounter keeps track of the resources processed for the current account
-type catchpointAccountResourceCounter struct {
- totalAppParams uint64
- totalAppLocalStates uint64
- totalAssetParams uint64
- totalAssets uint64
-}
-
-// encodedAccountsBatchIter allows us to iterate over the accounts data stored in the accountbase table.
-type encodedAccountsBatchIter struct {
- accountsRows *sql.Rows
- resourcesRows *sql.Rows
- nextBaseRow pendingBaseRow
- nextResourceRow pendingResourceRow
- acctResCnt catchpointAccountResourceCounter
-}
-
-// Next returns an array containing the account data, in the same way it appear in the database
-// returning accountCount accounts data at a time.
-func (iterator *encodedAccountsBatchIter) Next(ctx context.Context, tx *sql.Tx, accountCount int, resourceCount int) (bals []encodedBalanceRecordV6, numAccountsProcessed uint64, err error) {
- if iterator.accountsRows == nil {
- iterator.accountsRows, err = tx.QueryContext(ctx, "SELECT rowid, address, data FROM accountbase ORDER BY rowid")
- if err != nil {
- return
- }
- }
- if iterator.resourcesRows == nil {
- iterator.resourcesRows, err = tx.QueryContext(ctx, "SELECT addrid, aidx, data FROM resources ORDER BY addrid, aidx")
- if err != nil {
- return
- }
- }
-
- // gather up to accountCount encoded accounts.
- bals = make([]encodedBalanceRecordV6, 0, accountCount)
- var encodedRecord encodedBalanceRecordV6
- var baseAcct store.BaseAccountData
- var numAcct int
- baseCb := func(addr basics.Address, rowid int64, accountData *store.BaseAccountData, encodedAccountData []byte) (err error) {
- encodedRecord = encodedBalanceRecordV6{Address: addr, AccountData: encodedAccountData}
- baseAcct = *accountData
- numAcct++
- return nil
- }
-
- var totalResources int
-
- // emptyCount := 0
- resCb := func(addr basics.Address, cidx basics.CreatableIndex, resData *store.ResourcesData, encodedResourceData []byte, lastResource bool) error {
-
- emptyBaseAcct := baseAcct.TotalAppParams == 0 && baseAcct.TotalAppLocalStates == 0 && baseAcct.TotalAssetParams == 0 && baseAcct.TotalAssets == 0
- if !emptyBaseAcct && resData != nil {
- if encodedRecord.Resources == nil {
- encodedRecord.Resources = make(map[uint64]msgp.Raw)
- }
- encodedRecord.Resources[uint64(cidx)] = encodedResourceData
- if resData.IsApp() && resData.IsOwning() {
- iterator.acctResCnt.totalAppParams++
- }
- if resData.IsApp() && resData.IsHolding() {
- iterator.acctResCnt.totalAppLocalStates++
- }
-
- if resData.IsAsset() && resData.IsOwning() {
- iterator.acctResCnt.totalAssetParams++
- }
- if resData.IsAsset() && resData.IsHolding() {
- iterator.acctResCnt.totalAssets++
- }
- totalResources++
- }
-
- if baseAcct.TotalAppParams == iterator.acctResCnt.totalAppParams &&
- baseAcct.TotalAppLocalStates == iterator.acctResCnt.totalAppLocalStates &&
- baseAcct.TotalAssetParams == iterator.acctResCnt.totalAssetParams &&
- baseAcct.TotalAssets == iterator.acctResCnt.totalAssets {
-
- encodedRecord.ExpectingMoreEntries = false
- bals = append(bals, encodedRecord)
- numAccountsProcessed++
-
- iterator.acctResCnt = catchpointAccountResourceCounter{}
-
- return nil
- }
-
- // max resources per chunk reached, stop iterating.
- if lastResource {
- encodedRecord.ExpectingMoreEntries = true
- bals = append(bals, encodedRecord)
- encodedRecord.Resources = nil
- }
-
- return nil
- }
-
- _, iterator.nextBaseRow, iterator.nextResourceRow, err = processAllBaseAccountRecords(
- iterator.accountsRows, iterator.resourcesRows,
- baseCb, resCb,
- iterator.nextBaseRow, iterator.nextResourceRow, accountCount, resourceCount,
- )
- if err != nil {
- iterator.Close()
- return
- }
-
- if len(bals) == accountCount || totalResources == resourceCount {
- // we're done with this iteration.
- return
- }
-
- err = iterator.accountsRows.Err()
- if err != nil {
- iterator.Close()
- return
- }
- // Do not Close() the iterator here. It is the caller's responsibility to
- // do so, signalled by the return of an empty chunk. If we Close() here, the
- // next call to Next() will start all over!
- return
-}
-
-// Close shuts down the encodedAccountsBatchIter, releasing database resources.
-func (iterator *encodedAccountsBatchIter) Close() {
- if iterator.accountsRows != nil {
- iterator.accountsRows.Close()
- iterator.accountsRows = nil
- }
- if iterator.resourcesRows != nil {
- iterator.resourcesRows.Close()
- iterator.resourcesRows = nil
- }
-}
-
-// orderedAccountsIterStep is used by orderedAccountsIter to define the current step
-//
-//msgp:ignore orderedAccountsIterStep
-type orderedAccountsIterStep int
-
-const (
- // startup step
- oaiStepStartup = orderedAccountsIterStep(0)
- // delete old ordering table if we have any leftover from previous invocation
- oaiStepDeleteOldOrderingTable = orderedAccountsIterStep(0)
- // create new ordering table
- oaiStepCreateOrderingTable = orderedAccountsIterStep(1)
- // query the existing accounts
- oaiStepQueryAccounts = orderedAccountsIterStep(2)
- // iterate over the existing accounts and insert their hash & address into the staging ordering table
- oaiStepInsertAccountData = orderedAccountsIterStep(3)
- // create an index on the ordering table so that we can efficiently scan it.
- oaiStepCreateOrderingAccountIndex = orderedAccountsIterStep(4)
- // query the ordering table
- oaiStepSelectFromOrderedTable = orderedAccountsIterStep(5)
- // iterate over the ordering table
- oaiStepIterateOverOrderedTable = orderedAccountsIterStep(6)
- // cleanup and delete ordering table
- oaiStepShutdown = orderedAccountsIterStep(7)
- // do nothing as we're done.
- oaiStepDone = orderedAccountsIterStep(8)
-)
-
-// orderedAccountsIter allows us to iterate over the accounts addresses in the order of the account hashes.
-type orderedAccountsIter struct {
- step orderedAccountsIterStep
- accountBaseRows *sql.Rows
- hashesRows *sql.Rows
- resourcesRows *sql.Rows
- tx *sql.Tx
- pendingBaseRow pendingBaseRow
- pendingResourceRow pendingResourceRow
- accountCount int
- insertStmt *sql.Stmt
-}
-
-// makeOrderedAccountsIter creates an ordered account iterator. Note that due to implementation reasons,
-// only a single iterator can be active at a time.
-func makeOrderedAccountsIter(tx *sql.Tx, accountCount int) *orderedAccountsIter {
- return &orderedAccountsIter{
- tx: tx,
- accountCount: accountCount,
- step: oaiStepStartup,
- }
-}
-
-type pendingBaseRow struct {
- addr basics.Address
- rowid int64
- accountData *store.BaseAccountData
- encodedAccountData []byte
-}
-
-type pendingResourceRow struct {
- addrid int64
- aidx basics.CreatableIndex
- buf []byte
-}
-
-func processAllResources(
- resRows *sql.Rows,
- addr basics.Address, accountData *store.BaseAccountData, acctRowid int64, pr pendingResourceRow, resourceCount int,
- callback func(addr basics.Address, creatableIdx basics.CreatableIndex, resData *store.ResourcesData, encodedResourceData []byte, lastResource bool) error,
-) (pendingResourceRow, int, error) {
- var err error
- count := 0
-
- // Declare variabled outside of the loop to prevent allocations per iteration.
- // At least resData is resolved as "escaped" because of passing it by a pointer to protocol.Decode()
- var buf []byte
- var addrid int64
- var aidx basics.CreatableIndex
- var resData store.ResourcesData
- for {
- if pr.addrid != 0 {
- // some accounts may not have resources, consider the following case:
- // acct 1 and 3 has resources, account 2 does not
- // in this case addrid = 3 after processing resources from 1, but acctRowid = 2
- // and we need to skip accounts without resources
- if pr.addrid > acctRowid {
- err = callback(addr, 0, nil, nil, false)
- return pr, count, err
- }
- if pr.addrid < acctRowid {
- err = fmt.Errorf("resource table entries mismatches accountbase table entries : reached addrid %d while expecting resource for %d", pr.addrid, acctRowid)
- return pendingResourceRow{}, count, err
- }
- addrid = pr.addrid
- buf = pr.buf
- aidx = pr.aidx
- pr = pendingResourceRow{}
- } else {
- if !resRows.Next() {
- err = callback(addr, 0, nil, nil, false)
- if err != nil {
- return pendingResourceRow{}, count, err
- }
- break
- }
- err = resRows.Scan(&addrid, &aidx, &buf)
- if err != nil {
- return pendingResourceRow{}, count, err
- }
- if addrid < acctRowid {
- err = fmt.Errorf("resource table entries mismatches accountbase table entries : reached addrid %d while expecting resource for %d", addrid, acctRowid)
- return pendingResourceRow{}, count, err
- } else if addrid > acctRowid {
- err = callback(addr, 0, nil, nil, false)
- return pendingResourceRow{addrid, aidx, buf}, count, err
- }
- }
- resData = store.ResourcesData{}
- err = protocol.Decode(buf, &resData)
- if err != nil {
- return pendingResourceRow{}, count, err
- }
- count++
- if resourceCount > 0 && count == resourceCount {
- // last resource to be included in chunk
- err := callback(addr, aidx, &resData, buf, true)
- return pendingResourceRow{}, count, err
- }
- err = callback(addr, aidx, &resData, buf, false)
- if err != nil {
- return pendingResourceRow{}, count, err
- }
- }
- return pendingResourceRow{}, count, nil
-}
-
-func processAllBaseAccountRecords(
- baseRows *sql.Rows,
- resRows *sql.Rows,
- baseCb func(addr basics.Address, rowid int64, accountData *store.BaseAccountData, encodedAccountData []byte) error,
- resCb func(addr basics.Address, creatableIdx basics.CreatableIndex, resData *store.ResourcesData, encodedResourceData []byte, lastResource bool) error,
- pendingBase pendingBaseRow, pendingResource pendingResourceRow, accountCount int, resourceCount int,
-) (int, pendingBaseRow, pendingResourceRow, error) {
- var addr basics.Address
- var prevAddr basics.Address
- var err error
- count := 0
-
- var accountData store.BaseAccountData
- var addrbuf []byte
- var buf []byte
- var rowid int64
- for {
- if pendingBase.rowid != 0 {
- addr = pendingBase.addr
- rowid = pendingBase.rowid
- accountData = *pendingBase.accountData
- buf = pendingBase.encodedAccountData
- pendingBase = pendingBaseRow{}
- } else {
- if !baseRows.Next() {
- break
- }
-
- err = baseRows.Scan(&rowid, &addrbuf, &buf)
- if err != nil {
- return 0, pendingBaseRow{}, pendingResourceRow{}, err
- }
-
- if len(addrbuf) != len(addr) {
- err = fmt.Errorf("account DB address length mismatch: %d != %d", len(addrbuf), len(addr))
- return 0, pendingBaseRow{}, pendingResourceRow{}, err
- }
-
- copy(addr[:], addrbuf)
-
- accountData = store.BaseAccountData{}
- err = protocol.Decode(buf, &accountData)
- if err != nil {
- return 0, pendingBaseRow{}, pendingResourceRow{}, err
- }
- }
-
- err = baseCb(addr, rowid, &accountData, buf)
- if err != nil {
- return 0, pendingBaseRow{}, pendingResourceRow{}, err
- }
-
- var resourcesProcessed int
- pendingResource, resourcesProcessed, err = processAllResources(resRows, addr, &accountData, rowid, pendingResource, resourceCount, resCb)
- if err != nil {
- err = fmt.Errorf("failed to gather resources for account %v, addrid %d, prev address %v : %w", addr, rowid, prevAddr, err)
- return 0, pendingBaseRow{}, pendingResourceRow{}, err
- }
-
- if resourcesProcessed == resourceCount {
- // we're done with this iteration.
- pendingBase := pendingBaseRow{
- addr: addr,
- rowid: rowid,
- accountData: &accountData,
- encodedAccountData: buf,
- }
- return count, pendingBase, pendingResource, nil
- }
- resourceCount -= resourcesProcessed
-
- count++
- if accountCount > 0 && count == accountCount {
- // we're done with this iteration.
- return count, pendingBaseRow{}, pendingResource, nil
- }
- prevAddr = addr
- }
-
- return count, pendingBaseRow{}, pendingResource, nil
-}
-
-// accountAddressHash is used by Next to return a single account address and the associated hash.
-type accountAddressHash struct {
- addrid int64
- digest []byte
-}
-
-// Next returns an array containing the account address and hash
-// the Next function works in multiple processing stages, where it first processes the current accounts and order them
-// followed by returning the ordered accounts. In the first phase, it would return empty accountAddressHash array
-// and sets the processedRecords to the number of accounts that were processed. On the second phase, the acct
-// would contain valid data ( and optionally the account data as well, if was asked in makeOrderedAccountsIter) and
-// the processedRecords would be zero. If err is sql.ErrNoRows it means that the iterator have completed it's work and no further
-// accounts exists. Otherwise, the caller is expected to keep calling "Next" to retrieve the next set of accounts
-// ( or let the Next function make some progress toward that goal )
-func (iterator *orderedAccountsIter) Next(ctx context.Context) (acct []accountAddressHash, processedRecords int, err error) {
- if iterator.step == oaiStepDeleteOldOrderingTable {
- // although we're going to delete this table anyway when completing the iterator execution, we'll try to
- // clean up any intermediate table.
- _, err = iterator.tx.ExecContext(ctx, "DROP TABLE IF EXISTS accountsiteratorhashes")
- if err != nil {
- return
- }
- iterator.step = oaiStepCreateOrderingTable
- return
- }
- if iterator.step == oaiStepCreateOrderingTable {
- // create the temporary table
- _, err = iterator.tx.ExecContext(ctx, "CREATE TABLE accountsiteratorhashes(addrid INTEGER, hash blob)")
- if err != nil {
- return
- }
- iterator.step = oaiStepQueryAccounts
- return
- }
- if iterator.step == oaiStepQueryAccounts {
- // iterate over the existing accounts
- iterator.accountBaseRows, err = iterator.tx.QueryContext(ctx, "SELECT rowid, address, data FROM accountbase ORDER BY rowid")
- if err != nil {
- return
- }
- // iterate over the existing resources
- iterator.resourcesRows, err = iterator.tx.QueryContext(ctx, "SELECT addrid, aidx, data FROM resources ORDER BY addrid, aidx")
- if err != nil {
- return
- }
- // prepare the insert statement into the temporary table
- iterator.insertStmt, err = iterator.tx.PrepareContext(ctx, "INSERT INTO accountsiteratorhashes(addrid, hash) VALUES(?, ?)")
- if err != nil {
- return
- }
- iterator.step = oaiStepInsertAccountData
- return
- }
- if iterator.step == oaiStepInsertAccountData {
- var lastAddrID int64
- baseCb := func(addr basics.Address, rowid int64, accountData *store.BaseAccountData, encodedAccountData []byte) (err error) {
- hash := store.AccountHashBuilderV6(addr, accountData, encodedAccountData)
- _, err = iterator.insertStmt.ExecContext(ctx, rowid, hash)
- if err != nil {
- return
- }
- lastAddrID = rowid
- return nil
- }
-
- resCb := func(addr basics.Address, cidx basics.CreatableIndex, resData *store.ResourcesData, encodedResourceData []byte, lastResource bool) error {
- if resData != nil {
- hash, err := store.ResourcesHashBuilderV6(resData, addr, cidx, resData.UpdateRound, encodedResourceData)
- if err != nil {
- return err
- }
- _, err = iterator.insertStmt.ExecContext(ctx, lastAddrID, hash)
- return err
- }
- return nil
- }
-
- count := 0
- count, iterator.pendingBaseRow, iterator.pendingResourceRow, err = processAllBaseAccountRecords(
- iterator.accountBaseRows, iterator.resourcesRows,
- baseCb, resCb,
- iterator.pendingBaseRow, iterator.pendingResourceRow, iterator.accountCount, math.MaxInt,
- )
- if err != nil {
- iterator.Close(ctx)
- return
- }
-
- if count == iterator.accountCount {
- // we're done with this iteration.
- processedRecords = count
- return
- }
-
- // make sure the resource iterator has no more entries.
- if iterator.resourcesRows.Next() {
- iterator.Close(ctx)
- err = errors.New("resource table entries exceed the ones specified in the accountbase table")
- return
- }
-
- processedRecords = count
- iterator.accountBaseRows.Close()
- iterator.accountBaseRows = nil
- iterator.resourcesRows.Close()
- iterator.resourcesRows = nil
- iterator.insertStmt.Close()
- iterator.insertStmt = nil
- iterator.step = oaiStepCreateOrderingAccountIndex
- return
- }
- if iterator.step == oaiStepCreateOrderingAccountIndex {
- // create an index. It shown that even when we're making a single select statement in step 5, it would be better to have this index vs. not having it at all.
- // note that this index is using the rowid of the accountsiteratorhashes table.
- _, err = iterator.tx.ExecContext(ctx, "CREATE INDEX accountsiteratorhashesidx ON accountsiteratorhashes(hash)")
- if err != nil {
- iterator.Close(ctx)
- return
- }
- iterator.step = oaiStepSelectFromOrderedTable
- return
- }
- if iterator.step == oaiStepSelectFromOrderedTable {
- // select the data from the ordered table
- iterator.hashesRows, err = iterator.tx.QueryContext(ctx, "SELECT addrid, hash FROM accountsiteratorhashes ORDER BY hash")
-
- if err != nil {
- iterator.Close(ctx)
- return
- }
- iterator.step = oaiStepIterateOverOrderedTable
- return
- }
-
- if iterator.step == oaiStepIterateOverOrderedTable {
- acct = make([]accountAddressHash, iterator.accountCount)
- acctIdx := 0
- for iterator.hashesRows.Next() {
- err = iterator.hashesRows.Scan(&(acct[acctIdx].addrid), &(acct[acctIdx].digest))
- if err != nil {
- iterator.Close(ctx)
- return
- }
- acctIdx++
- if acctIdx == iterator.accountCount {
- // we're done with this iteration.
- return
- }
- }
- acct = acct[:acctIdx]
- iterator.step = oaiStepShutdown
- iterator.hashesRows.Close()
- iterator.hashesRows = nil
- return
- }
- if iterator.step == oaiStepShutdown {
- err = iterator.Close(ctx)
- if err != nil {
- return
- }
- iterator.step = oaiStepDone
- // fallthrough
- }
- return nil, 0, sql.ErrNoRows
-}
-
-// Close shuts down the orderedAccountsBuilderIter, releasing database resources.
-func (iterator *orderedAccountsIter) Close(ctx context.Context) (err error) {
- if iterator.accountBaseRows != nil {
- iterator.accountBaseRows.Close()
- iterator.accountBaseRows = nil
- }
- if iterator.resourcesRows != nil {
- iterator.resourcesRows.Close()
- iterator.resourcesRows = nil
- }
- if iterator.hashesRows != nil {
- iterator.hashesRows.Close()
- iterator.hashesRows = nil
- }
- if iterator.insertStmt != nil {
- iterator.insertStmt.Close()
- iterator.insertStmt = nil
- }
- _, err = iterator.tx.ExecContext(ctx, "DROP TABLE IF EXISTS accountsiteratorhashes")
- return
-}
-
-// catchpointPendingHashesIterator allows us to iterate over the hashes in the catchpointpendinghashes table in their order.
-type catchpointPendingHashesIterator struct {
- hashCount int
- tx *sql.Tx
- rows *sql.Rows
-}
-
-// makeCatchpointPendingHashesIterator create a pending hashes iterator that retrieves the hashes in the catchpointpendinghashes table.
-func makeCatchpointPendingHashesIterator(hashCount int, tx *sql.Tx) *catchpointPendingHashesIterator {
- return &catchpointPendingHashesIterator{
- hashCount: hashCount,
- tx: tx,
- }
-}
-
-// Next returns an array containing the hashes, returning HashCount hashes at a time.
-func (iterator *catchpointPendingHashesIterator) Next(ctx context.Context) (hashes [][]byte, err error) {
- if iterator.rows == nil {
- iterator.rows, err = iterator.tx.QueryContext(ctx, "SELECT data FROM catchpointpendinghashes ORDER BY data")
- if err != nil {
- return
- }
- }
-
- // gather up to accountCount encoded accounts.
- hashes = make([][]byte, iterator.hashCount)
- hashIdx := 0
- for iterator.rows.Next() {
- err = iterator.rows.Scan(&hashes[hashIdx])
- if err != nil {
- iterator.Close()
- return
- }
-
- hashIdx++
- if hashIdx == iterator.hashCount {
- // we're done with this iteration.
- return
- }
- }
- hashes = hashes[:hashIdx]
- err = iterator.rows.Err()
- if err != nil {
- iterator.Close()
- return
- }
- // we just finished reading the table.
- iterator.Close()
- return
-}
-
-// Close shuts down the catchpointPendingHashesIterator, releasing database resources.
-func (iterator *catchpointPendingHashesIterator) Close() {
- if iterator.rows != nil {
- iterator.rows.Close()
- iterator.rows = nil
- }
-}
diff --git a/ledger/accountdb_test.go b/ledger/acctdeltas_test.go
index c290ed827..21ac1e5dc 100644
--- a/ledger/accountdb_test.go
+++ b/ledger/acctdeltas_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -32,14 +32,14 @@ import (
"testing"
"time"
- "github.com/algorand/go-algorand/data/transactions/logic"
-
"github.com/stretchr/testify/require"
+ "github.com/algorand/avm-abi/apps"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/crypto/merklesignature"
"github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/ledger/encoded"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store"
storetesting "github.com/algorand/go-algorand/ledger/store/testing"
@@ -905,9 +905,9 @@ func benchmarkWriteCatchpointStagingBalancesSub(b *testing.B, ascendingOrder boo
last64KAccountCreationTime = time.Duration(0)
}
var chunk catchpointFileChunkV6
- chunk.Balances = make([]encodedBalanceRecordV6, chunkSize)
+ chunk.Balances = make([]encoded.BalanceRecordV6, chunkSize)
for i := uint64(0); i < chunkSize; i++ {
- var randomAccount encodedBalanceRecordV6
+ var randomAccount encoded.BalanceRecordV6
accountData := store.BaseAccountData{RewardsBase: accountsLoaded + i}
accountData.MicroAlgos.Raw = crypto.RandUint63()
randomAccount.AccountData = protocol.Encode(&accountData)
@@ -1180,12 +1180,12 @@ func BenchmarkLookupKeyByPrefix(b *testing.B) {
crypto.RandBytes(nameBuffer)
crypto.RandBytes(valueBuffer)
appID := basics.AppIndex(crypto.RandUint64())
- boxKey := logic.MakeBoxKey(appID, string(nameBuffer))
+ boxKey := apps.MakeBoxKey(uint64(appID), string(nameBuffer))
err = writer.UpsertKvPair(boxKey, valueBuffer)
require.NoError(b, err)
if i == 0 {
- prefix = logic.MakeBoxKey(appID, "")
+ prefix = apps.MakeBoxKey(uint64(appID), "")
}
}
err = tx.Commit()
diff --git a/ledger/acctonline.go b/ledger/acctonline.go
index bc300cf1f..f73b153e2 100644
--- a/ledger/acctonline.go
+++ b/ledger/acctonline.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -360,8 +360,6 @@ func (ao *onlineAccounts) prepareCommit(dcc *deferredCommitContext) error {
// Index that corresponds to the oldest round still in deltas
startIndex := len(ao.onlineRoundParamsData) - len(ao.deltas) - 1
if ao.onlineRoundParamsData[startIndex+1].CurrentProtocol != ao.onlineRoundParamsData[startIndex+int(offset)].CurrentProtocol {
- ao.accountsMu.RUnlock()
-
// in scheduleCommit, we expect that this function to update the catchpointWriting when
// it's on a catchpoint round and the node is configured to generate catchpoints. Doing this in a deferred function
// here would prevent us from "forgetting" to update this variable later on.
@@ -539,7 +537,6 @@ func (ao *onlineAccounts) onlineTotalsEx(rnd basics.Round) (basics.MicroAlgos, e
func (ao *onlineAccounts) onlineTotalsImpl(rnd basics.Round) (basics.MicroAlgos, error) {
offset, err := ao.roundParamsOffset(rnd)
if err != nil {
- ao.log.Warnf("onlineAccounts failed to fetch online totals for rnd: %d", rnd)
return basics.MicroAlgos{}, err
}
@@ -551,7 +548,6 @@ func (ao *onlineAccounts) onlineTotalsImpl(rnd basics.Round) (basics.MicroAlgos,
func (ao *onlineAccounts) LookupOnlineAccountData(rnd basics.Round, addr basics.Address) (data basics.OnlineAccountData, err error) {
oad, err := ao.lookupOnlineAccountData(rnd, addr)
if err != nil {
- ao.log.Warnf("onlineAccounts failed to fetch online account data for rnd: %d, addr: %v", rnd, addr)
return
}
diff --git a/ledger/acctonline_test.go b/ledger/acctonline_test.go
index 9debfc9e2..d892c21b6 100644
--- a/ledger/acctonline_test.go
+++ b/ledger/acctonline_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/acctupdates.go b/ledger/acctupdates.go
index c2cfc69fc..dbaaefca6 100644
--- a/ledger/acctupdates.go
+++ b/ledger/acctupdates.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go
index 7329c61e1..723e2c198 100644
--- a/ledger/acctupdates_test.go
+++ b/ledger/acctupdates_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -31,11 +31,11 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/algorand/avm-abi/apps"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
- "github.com/algorand/go-algorand/data/transactions/logic"
"github.com/algorand/go-algorand/ledger/internal"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store"
@@ -1290,7 +1290,7 @@ func TestBoxNamesByAppIDs(t *testing.T) {
boxChange := ledgercore.KvValueDelta{Data: []byte(boxName)}
auNewBlock(t, currentRound, au, accts, opts, map[string]ledgercore.KvValueDelta{
- logic.MakeBoxKey(appID, boxName): boxChange,
+ apps.MakeBoxKey(uint64(appID), boxName): boxChange,
})
auCommitSync(t, currentRound, au, ml)
@@ -1305,10 +1305,10 @@ func TestBoxNamesByAppIDs(t *testing.T) {
// check input, see all present keys are all still there
for _, storedBoxName := range testingBoxNames[:i+1] {
- res, err := au.LookupKeysByPrefix(currentRound, logic.MakeBoxKey(boxNameToAppID[storedBoxName], ""), 10000)
+ res, err := au.LookupKeysByPrefix(currentRound, apps.MakeBoxKey(uint64(boxNameToAppID[storedBoxName]), ""), 10000)
require.NoError(t, err)
require.Len(t, res, 1)
- require.Equal(t, logic.MakeBoxKey(boxNameToAppID[storedBoxName], storedBoxName), res[0])
+ require.Equal(t, apps.MakeBoxKey(uint64(boxNameToAppID[storedBoxName]), storedBoxName), res[0])
}
}
@@ -1319,12 +1319,12 @@ func TestBoxNamesByAppIDs(t *testing.T) {
// remove inserted box
appID := boxNameToAppID[boxName]
auNewBlock(t, currentRound, au, accts, opts, map[string]ledgercore.KvValueDelta{
- logic.MakeBoxKey(appID, boxName): {},
+ apps.MakeBoxKey(uint64(appID), boxName): {},
})
auCommitSync(t, currentRound, au, ml)
// ensure recently removed key is not present, and it is not part of the result
- res, err := au.LookupKeysByPrefix(currentRound, logic.MakeBoxKey(boxNameToAppID[boxName], ""), 10000)
+ res, err := au.LookupKeysByPrefix(currentRound, apps.MakeBoxKey(uint64(boxNameToAppID[boxName]), ""), 10000)
require.NoError(t, err)
require.Len(t, res, 0)
}
diff --git a/ledger/applications_test.go b/ledger/applications_test.go
index 1d27bb9ed..1887da254 100644
--- a/ledger/applications_test.go
+++ b/ledger/applications_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/application.go b/ledger/apply/application.go
index e02af73c5..3d03349eb 100644
--- a/ledger/apply/application.go
+++ b/ledger/apply/application.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/application_test.go b/ledger/apply/application_test.go
index e9e9f699c..bc8eb74ae 100644
--- a/ledger/apply/application_test.go
+++ b/ledger/apply/application_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/apply.go b/ledger/apply/apply.go
index 34017e383..b694d9ed5 100644
--- a/ledger/apply/apply.go
+++ b/ledger/apply/apply.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/asset.go b/ledger/apply/asset.go
index 53b4bbfaf..a29dd7ff5 100644
--- a/ledger/apply/asset.go
+++ b/ledger/apply/asset.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/asset_test.go b/ledger/apply/asset_test.go
index c46d6363f..2b7908ac6 100644
--- a/ledger/apply/asset_test.go
+++ b/ledger/apply/asset_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/keyreg.go b/ledger/apply/keyreg.go
index 4c7155d71..206ff31bf 100644
--- a/ledger/apply/keyreg.go
+++ b/ledger/apply/keyreg.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/keyreg_test.go b/ledger/apply/keyreg_test.go
index 09699e3f3..7589b9fb1 100644
--- a/ledger/apply/keyreg_test.go
+++ b/ledger/apply/keyreg_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/mockBalances_test.go b/ledger/apply/mockBalances_test.go
index 8ac970177..a9b838715 100644
--- a/ledger/apply/mockBalances_test.go
+++ b/ledger/apply/mockBalances_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/payment.go b/ledger/apply/payment.go
index c86f791e5..ad8f128e9 100644
--- a/ledger/apply/payment.go
+++ b/ledger/apply/payment.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/payment_test.go b/ledger/apply/payment_test.go
index 6f20dd9a5..db82869e2 100644
--- a/ledger/apply/payment_test.go
+++ b/ledger/apply/payment_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apply/stateproof.go b/ledger/apply/stateproof.go
index fca56c0f3..dba21a724 100644
--- a/ledger/apply/stateproof.go
+++ b/ledger/apply/stateproof.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go
index 194c6e84a..a08a3b74b 100644
--- a/ledger/apptxn_test.go
+++ b/ledger/apptxn_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/archival_test.go b/ledger/archival_test.go
index 50d367d65..c7f00b7d6 100644
--- a/ledger/archival_test.go
+++ b/ledger/archival_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -29,12 +29,11 @@ import (
"github.com/stretchr/testify/require"
- "github.com/algorand/go-deadlock"
-
"github.com/algorand/go-algorand/agreement"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
@@ -111,8 +110,8 @@ func getInitState() (genesisInitState ledgercore.InitState) {
blk.FeeSink = testSinkAddr
accts := make(map[basics.Address]basics.AccountData)
- accts[testPoolAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 1234567890})
- accts[testSinkAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 1234567890})
+ accts[testPoolAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 1234567890})
+ accts[testSinkAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 1234567890})
genesisInitState.Accounts = accts
genesisInitState.Block = blk
@@ -191,13 +190,6 @@ func TestArchivalRestart(t *testing.T) {
// Start in archival mode, add 2K blocks, restart, ensure all blocks are there
- // disable deadlock checking code
- deadlockDisable := deadlock.Opts.Disable
- deadlock.Opts.Disable = true
- defer func() {
- deadlock.Opts.Disable = deadlockDisable
- }()
-
dbName := fmt.Sprintf("%s.%d", t.Name(), crypto.RandUint64())
dbPrefix := filepath.Join(t.TempDir(), dbName)
@@ -338,13 +330,6 @@ func TestArchivalCreatables(t *testing.T) {
// restart, ensure all assets are there in index unless they were
// deleted
- // disable deadlock checking code
- deadlockDisable := deadlock.Opts.Disable
- deadlock.Opts.Disable = true
- defer func() {
- deadlock.Opts.Disable = deadlockDisable
- }()
-
dbName := fmt.Sprintf("%s.%d", t.Name(), crypto.RandUint64())
dbPrefix := filepath.Join(t.TempDir(), dbName)
@@ -377,7 +362,7 @@ func TestArchivalCreatables(t *testing.T) {
_, err := rand.Read(creator[:])
require.NoError(t, err)
creators = append(creators, creator)
- genesisInitState.Accounts[creator] = basics.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890})
+ genesisInitState.Accounts[creator] = basics_testing.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890})
}
// open ledger
@@ -690,11 +675,6 @@ func TestArchivalFromNonArchival(t *testing.T) {
partitiontest.PartitionTest(t)
// Start in non-archival mode, add 2K blocks, restart in archival mode ensure only genesis block is there
- deadlockDisable := deadlock.Opts.Disable
- deadlock.Opts.Disable = true
- defer func() {
- deadlock.Opts.Disable = deadlockDisable
- }()
dbName := fmt.Sprintf("%s.%d", t.Name(), crypto.RandUint64())
dbPrefix := filepath.Join(t.TempDir(), dbName)
@@ -712,7 +692,7 @@ func TestArchivalFromNonArchival(t *testing.T) {
addr := basics.Address{}
_, err := rand.Read(addr[:])
require.NoError(t, err)
- br := basics.BalanceRecord{AccountData: basics.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890}), Addr: addr}
+ br := basics.BalanceRecord{AccountData: basics_testing.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890}), Addr: addr}
genesisInitState.Accounts[addr] = br.AccountData
balanceRecords = append(balanceRecords, br)
}
diff --git a/ledger/blockHeaderCache.go b/ledger/blockHeaderCache.go
index 5e2be47d2..b0f27f78e 100644
--- a/ledger/blockHeaderCache.go
+++ b/ledger/blockHeaderCache.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/blockHeaderCache_test.go b/ledger/blockHeaderCache_test.go
index a5fac5ae6..6728e71c6 100644
--- a/ledger/blockHeaderCache_test.go
+++ b/ledger/blockHeaderCache_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/blockqueue.go b/ledger/blockqueue.go
index 4dfa1ae2c..a72c1d8cb 100644
--- a/ledger/blockqueue.go
+++ b/ledger/blockqueue.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -52,10 +52,29 @@ type blockQueue struct {
closed chan struct{}
}
-func bqInit(l *Ledger) (*blockQueue, error) {
+func newBlockQueue(l *Ledger) (*blockQueue, error) {
bq := &blockQueue{}
bq.cond = sync.NewCond(&bq.mu)
bq.l = l
+ return bq, nil
+}
+
+func (bq *blockQueue) start() error {
+ bq.mu.Lock()
+ defer bq.mu.Unlock()
+
+ if bq.running {
+ // this should be harmless, but it should also be impossible
+ bq.l.log.Warn("blockQueue.start() already started")
+ return nil
+ }
+ if bq.closed != nil {
+ // a previus close() is still waiting on a previous syncer() to finish
+ oldsyncer := bq.closed
+ bq.mu.Unlock()
+ <-oldsyncer
+ bq.mu.Lock()
+ }
bq.running = true
bq.closed = make(chan struct{})
ledgerBlockqInitCount.Inc(nil)
@@ -67,33 +86,32 @@ func bqInit(l *Ledger) (*blockQueue, error) {
})
ledgerBlockqInitMicros.AddMicrosecondsSince(start, nil)
if err != nil {
- return nil, err
+ return err
}
go bq.syncer()
- return bq, nil
+ return nil
}
-func (bq *blockQueue) close() {
+func (bq *blockQueue) stop() {
bq.mu.Lock()
- defer func() {
- bq.mu.Unlock()
- // we want to block here until the sync go routine is done.
- // it's not (just) for the sake of a complete cleanup, but rather
- // to ensure that the sync goroutine isn't busy in a notifyCommit
- // call which might be blocked inside one of the trackers.
- <-bq.closed
- }()
-
+ closechan := bq.closed
if bq.running {
bq.running = false
bq.cond.Broadcast()
}
-
+ bq.mu.Unlock()
+
+ // we want to block here until the sync go routine is done.
+ // it's not (just) for the sake of a complete cleanup, but rather
+ // to ensure that the sync goroutine isn't busy in a notifyCommit
+ // call which might be blocked inside one of the trackers.
+ if closechan != nil {
+ <-closechan
+ }
}
func (bq *blockQueue) syncer() {
- defer close(bq.closed)
bq.mu.Lock()
for {
for bq.running && len(bq.q) == 0 {
@@ -101,6 +119,8 @@ func (bq *blockQueue) syncer() {
}
if !bq.running {
+ close(bq.closed)
+ bq.closed = nil
bq.mu.Unlock()
return
}
diff --git a/ledger/blockqueue_test.go b/ledger/blockqueue_test.go
index 90aa7b4fe..2d702cfb9 100644
--- a/ledger/blockqueue_test.go
+++ b/ledger/blockqueue_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/boxtxn_test.go b/ledger/boxtxn_test.go
index 92f188632..f9099a4f9 100644
--- a/ledger/boxtxn_test.go
+++ b/ledger/boxtxn_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/bulletin.go b/ledger/bulletin.go
index 1e158aa80..5968c7f4f 100644
--- a/ledger/bulletin.go
+++ b/ledger/bulletin.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -81,7 +81,10 @@ func (b *bulletin) Wait(round basics.Round) chan struct{} {
}
func (b *bulletin) loadFromDisk(l ledgerForTracker, _ basics.Round) error {
- b.pendingNotificationRequests = make(map[basics.Round]notifier)
+ // We want to keep existing notification requests in memory if this flow is triggered by reloadLedger.
+ if b.pendingNotificationRequests == nil {
+ b.pendingNotificationRequests = make(map[basics.Round]notifier)
+ }
b.latestRound = l.Latest()
return nil
}
diff --git a/ledger/bulletin_test.go b/ledger/bulletin_test.go
index a3d5ae1f9..5a6f6bb83 100644
--- a/ledger/bulletin_test.go
+++ b/ledger/bulletin_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -17,9 +17,10 @@
package ledger
import (
- "github.com/algorand/go-algorand/test/partitiontest"
"testing"
"time"
+
+ "github.com/algorand/go-algorand/test/partitiontest"
)
const epsilon = 5 * time.Millisecond
diff --git a/ledger/catchpointfileheader.go b/ledger/catchpointfileheader.go
index 3b4600270..21cd6dbd5 100644
--- a/ledger/catchpointfileheader.go
+++ b/ledger/catchpointfileheader.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/catchpointtracker.go b/ledger/catchpointtracker.go
index 2867b285c..6968f580a 100644
--- a/ledger/catchpointtracker.go
+++ b/ledger/catchpointtracker.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -1360,7 +1360,7 @@ func (ct *catchpointTracker) initializeHashes(ctx context.Context, tx *sql.Tx, r
if rootHash.IsZero() {
ct.log.Infof("initializeHashes rebuilding merkle trie for round %d", rnd)
- accountBuilderIt := makeOrderedAccountsIter(tx, trieRebuildAccountChunkSize)
+ accountBuilderIt := store.MakeOrderedAccountsIter(tx, trieRebuildAccountChunkSize)
defer accountBuilderIt.Close(ctx)
startTrieBuildTime := time.Now()
trieHashCount := 0
@@ -1380,18 +1380,18 @@ func (ct *catchpointTracker) initializeHashes(ctx context.Context, tx *sql.Tx, r
trieHashCount += len(accts)
pendingTrieHashes += len(accts)
for _, acct := range accts {
- added, err := trie.Add(acct.digest)
+ added, err := trie.Add(acct.Digest)
if err != nil {
return fmt.Errorf("initializeHashes was unable to add acct to trie: %v", err)
}
if !added {
// we need to translate the "addrid" into actual account address so that
// we can report the failure.
- addr, err := arw.LookupAccountAddressFromAddressID(ctx, acct.addrid)
+ addr, err := arw.LookupAccountAddressFromAddressID(ctx, acct.Addrid)
if err != nil {
- ct.log.Warnf("initializeHashes attempted to add duplicate acct hash '%s' to merkle trie for account id %d : %v", hex.EncodeToString(acct.digest), acct.addrid, err)
+ ct.log.Warnf("initializeHashes attempted to add duplicate acct hash '%s' to merkle trie for account id %d : %v", hex.EncodeToString(acct.Digest), acct.Addrid, err)
} else {
- ct.log.Warnf("initializeHashes attempted to add duplicate acct hash '%s' to merkle trie for account %v", hex.EncodeToString(acct.digest), addr)
+ ct.log.Warnf("initializeHashes attempted to add duplicate acct hash '%s' to merkle trie for account %v", hex.EncodeToString(acct.Digest), addr)
}
}
}
@@ -1431,15 +1431,13 @@ func (ct *catchpointTracker) initializeHashes(ctx context.Context, tx *sql.Tx, r
// Now add the kvstore hashes
pendingTrieHashes = 0
- kvs, err := tx.QueryContext(ctx, "SELECT key, value FROM kvstore")
+ kvs, err := store.MakeKVsIter(ctx, tx)
if err != nil {
return err
}
defer kvs.Close()
for kvs.Next() {
- var k []byte
- var v []byte
- err := kvs.Scan(&k, &v)
+ k, v, err := kvs.KeyValue()
if err != nil {
return err
}
diff --git a/ledger/catchpointtracker_test.go b/ledger/catchpointtracker_test.go
index 82a34888e..33cfe9bae 100644
--- a/ledger/catchpointtracker_test.go
+++ b/ledger/catchpointtracker_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/catchpointwriter.go b/ledger/catchpointwriter.go
index e204a8ae7..839a2f24c 100644
--- a/ledger/catchpointwriter.go
+++ b/ledger/catchpointwriter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -25,9 +25,7 @@ import (
"os"
"path/filepath"
- "github.com/algorand/msgp/msgp"
-
- "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/ledger/encoded"
"github.com/algorand/go-algorand/ledger/store"
"github.com/algorand/go-algorand/protocol"
)
@@ -41,11 +39,6 @@ const (
// 100,000 resources * 20KB/resource => roughly max 2GB per chunk if all of them are max'ed out apps.
// In reality most entries are asset holdings, and they are very small.
ResourcesPerCatchpointFileChunk = 100_000
-
- // resourcesPerCatchpointFileChunkBackwardCompatible is the old value for ResourcesPerCatchpointFileChunk.
- // Size of a single resource entry was underestimated to 300 bytes that holds only for assets and not for apps.
- // It is safe to remove after April, 2023 since we are only supporting catchpoint that are 6 months old.
- resourcesPerCatchpointFileChunkBackwardCompatible = 300_000
)
// catchpointWriter is the struct managing the persistence of accounts data into the catchpoint file.
@@ -65,65 +58,34 @@ type catchpointWriter struct {
chunkNum uint64
writtenBytes int64
biggestChunkLen uint64
- accountsIterator encodedAccountsBatchIter
+ accountsIterator accountsBatchIter
maxResourcesPerChunk int
accountsDone bool
- kvRows *sql.Rows
+ kvRows kvIter
}
-type encodedBalanceRecordV5 struct {
- _struct struct{} `codec:",omitempty,omitemptyarray"`
-
- Address basics.Address `codec:"pk,allocbound=crypto.DigestSize"`
- AccountData msgp.Raw `codec:"ad"` // encoding of basics.AccountData
+type kvIter interface {
+ Next() bool
+ KeyValue() ([]byte, []byte, error)
+ Close()
}
-type catchpointFileBalancesChunkV5 struct {
- _struct struct{} `codec:",omitempty,omitemptyarray"`
- Balances []encodedBalanceRecordV5 `codec:"bl,allocbound=BalancesPerCatchpointFileChunk"`
+type accountsBatchIter interface {
+ Next(ctx context.Context, tx *sql.Tx, accountCount int, resourceCount int) ([]encoded.BalanceRecordV6, uint64, error)
+ Close()
}
-// SortUint64 re-export this sort, which is implemented in basics, and being used by the msgp when
-// encoding the resources map below.
-type SortUint64 = basics.SortUint64
-
-type encodedBalanceRecordV6 struct {
- _struct struct{} `codec:",omitempty,omitemptyarray"`
-
- Address basics.Address `codec:"a,allocbound=crypto.DigestSize"`
- AccountData msgp.Raw `codec:"b"` // encoding of baseAccountData
- Resources map[uint64]msgp.Raw `codec:"c,allocbound=resourcesPerCatchpointFileChunkBackwardCompatible"` // map of resourcesData
-
- // flag indicating whether there are more records for the same account coming up
- ExpectingMoreEntries bool `codec:"e"`
-}
-
-// Adjust these to be big enough for boxes, but not directly tied to box values.
-const (
- // For boxes: "bx:<8 bytes><64 byte name>"
- encodedKVRecordV6MaxKeyLength = 128
-
- // For boxes: MaxBoxSize
- encodedKVRecordV6MaxValueLength = 32768
-
- // MaxEncodedKVDataSize is the max size of serialized KV entry, checked with TestEncodedKVDataSize.
- // Exact value is 32906 that is 10 bytes more than 32768 + 128
- MaxEncodedKVDataSize = 33000
-)
-
-type encodedKVRecordV6 struct {
- _struct struct{} `codec:",omitempty,omitemptyarray"`
-
- Key []byte `codec:"k,allocbound=encodedKVRecordV6MaxKeyLength"`
- Value []byte `codec:"v,allocbound=encodedKVRecordV6MaxValueLength"`
+type catchpointFileBalancesChunkV5 struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+ Balances []encoded.BalanceRecordV5 `codec:"bl,allocbound=BalancesPerCatchpointFileChunk"`
}
type catchpointFileChunkV6 struct {
_struct struct{} `codec:",omitempty,omitemptyarray"`
- Balances []encodedBalanceRecordV6 `codec:"bl,allocbound=BalancesPerCatchpointFileChunk"`
+ Balances []encoded.BalanceRecordV6 `codec:"bl,allocbound=BalancesPerCatchpointFileChunk"`
numAccounts uint64
- KVs []encodedKVRecordV6 `codec:"kv,allocbound=BalancesPerCatchpointFileChunk"`
+ KVs []encoded.KVRecordV6 `codec:"kv,allocbound=BalancesPerCatchpointFileChunk"`
}
func (chunk catchpointFileChunkV6) empty() bool {
@@ -166,6 +128,7 @@ func makeCatchpointWriter(ctx context.Context, filePath string, tx *sql.Tx, maxR
file: file,
compressor: compressor,
tar: tar,
+ accountsIterator: store.MakeEncodedAccoutsBatchIter(),
maxResourcesPerChunk: maxResourcesPerChunk,
}
return res, nil
@@ -323,22 +286,20 @@ func (cw *catchpointWriter) readDatabaseStep(ctx context.Context, tx *sql.Tx) er
// Create the *Rows iterator JIT
if cw.kvRows == nil {
- rows, err := tx.QueryContext(ctx, "SELECT key, value FROM kvstore")
+ rows, err := store.MakeKVsIter(ctx, tx)
if err != nil {
return err
}
cw.kvRows = rows
}
- kvrs := make([]encodedKVRecordV6, 0, BalancesPerCatchpointFileChunk)
+ kvrs := make([]encoded.KVRecordV6, 0, BalancesPerCatchpointFileChunk)
for cw.kvRows.Next() {
- var k []byte
- var v []byte
- err := cw.kvRows.Scan(&k, &v)
+ k, v, err := cw.kvRows.KeyValue()
if err != nil {
return err
}
- kvrs = append(kvrs, encodedKVRecordV6{Key: k, Value: v})
+ kvrs = append(kvrs, encoded.KVRecordV6{Key: k, Value: v})
if len(kvrs) == BalancesPerCatchpointFileChunk {
break
}
diff --git a/ledger/catchpointwriter_test.go b/ledger/catchpointwriter_test.go
index 0bdac4a2a..478df2bb2 100644
--- a/ledger/catchpointwriter_test.go
+++ b/ledger/catchpointwriter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -24,7 +24,6 @@ import (
"database/sql"
"fmt"
"io"
- "math"
"os"
"path/filepath"
"strconv"
@@ -33,13 +32,14 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/algorand/avm-abi/apps"
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/crypto/merkletrie"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions"
- "github.com/algorand/go-algorand/data/transactions/logic"
"github.com/algorand/go-algorand/data/txntest"
+ "github.com/algorand/go-algorand/ledger/encoded"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store"
ledgertesting "github.com/algorand/go-algorand/ledger/testing"
@@ -71,19 +71,19 @@ func TestCatchpointFileBalancesChunkEncoding(t *testing.T) {
for i := uint64(0); i < numResources; i++ {
resources[i] = encodedResourceData
}
- balance := encodedBalanceRecordV6{
+ balance := encoded.BalanceRecordV6{
Address: ledgertesting.RandomAddress(),
AccountData: encodedBaseAD,
Resources: resources,
}
- balances := make([]encodedBalanceRecordV6, numChunkEntries)
- kv := encodedKVRecordV6{
- Key: make([]byte, encodedKVRecordV6MaxKeyLength),
- Value: make([]byte, encodedKVRecordV6MaxValueLength),
+ balances := make([]encoded.BalanceRecordV6, numChunkEntries)
+ kv := encoded.KVRecordV6{
+ Key: make([]byte, encoded.KVRecordV6MaxKeyLength),
+ Value: make([]byte, encoded.KVRecordV6MaxValueLength),
}
crypto.RandBytes(kv.Key[:])
crypto.RandBytes(kv.Value[:])
- kvs := make([]encodedKVRecordV6, numChunkEntries)
+ kvs := make([]encoded.KVRecordV6, numChunkEntries)
for i := 0; i < numChunkEntries; i++ {
balances[i] = balance
@@ -493,7 +493,7 @@ func TestFullCatchpointWriterOverflowAccounts(t *testing.T) {
require.NoError(t, err)
require.Zero(t, h)
- iter := makeOrderedAccountsIter(tx, trieRebuildAccountChunkSize)
+ iter := store.MakeOrderedAccountsIter(tx, trieRebuildAccountChunkSize)
defer iter.Close(ctx)
for {
accts, _, err := iter.Next(ctx)
@@ -507,7 +507,7 @@ func TestFullCatchpointWriterOverflowAccounts(t *testing.T) {
if len(accts) > 0 {
for _, acct := range accts {
- added, err := trie.Add(acct.digest)
+ added, err := trie.Add(acct.Digest)
require.NoError(t, err)
require.True(t, added)
}
@@ -779,7 +779,7 @@ func TestCatchpointAfterTxns(t *testing.T) {
values, err = l.LookupKeysByPrefix(l.Latest(), "bx:", 10)
require.NoError(t, err)
require.Len(t, values, 1)
- v, err := l.LookupKv(l.Latest(), logic.MakeBoxKey(boxApp, "xxx"))
+ v, err := l.LookupKv(l.Latest(), apps.MakeBoxKey(uint64(boxApp), "xxx"))
require.NoError(t, err)
require.Equal(t, strings.Repeat("\x00", 24), string(v))
@@ -869,41 +869,7 @@ func TestCatchpointAfterBoxTxns(t *testing.T) {
values, err := l.LookupKeysByPrefix(l.Latest(), "bx:", 10)
require.NoError(t, err)
require.Len(t, values, 1)
- v, err := l.LookupKv(l.Latest(), logic.MakeBoxKey(boxApp, "xxx"))
+ v, err := l.LookupKv(l.Latest(), apps.MakeBoxKey(uint64(boxApp), "xxx"))
require.NoError(t, err)
require.Equal(t, strings.Repeat("f", 24), string(v))
}
-
-func TestEncodedKVRecordV6Allocbounds(t *testing.T) {
- partitiontest.PartitionTest(t)
- t.Parallel()
-
- for version, params := range config.Consensus {
- require.GreaterOrEqualf(t, uint64(encodedKVRecordV6MaxValueLength), params.MaxBoxSize, "Allocbound constant no longer valid as of consensus version %s", version)
- longestPossibleBoxName := string(make([]byte, params.MaxAppKeyLen))
- longestPossibleKey := logic.MakeBoxKey(basics.AppIndex(math.MaxUint64), longestPossibleBoxName)
- require.GreaterOrEqualf(t, encodedKVRecordV6MaxValueLength, len(longestPossibleKey), "Allocbound constant no longer valid as of consensus version %s", version)
- }
-}
-
-func TestEncodedKVDataSize(t *testing.T) {
- partitiontest.PartitionTest(t)
- t.Parallel()
-
- currentConsensusParams := config.Consensus[protocol.ConsensusCurrentVersion]
-
- require.GreaterOrEqual(t, encodedKVRecordV6MaxKeyLength, currentConsensusParams.MaxAppKeyLen)
- require.GreaterOrEqual(t, uint64(encodedKVRecordV6MaxValueLength), currentConsensusParams.MaxBoxSize)
-
- kvEntry := encodedKVRecordV6{
- Key: make([]byte, encodedKVRecordV6MaxKeyLength),
- Value: make([]byte, encodedKVRecordV6MaxValueLength),
- }
-
- crypto.RandBytes(kvEntry.Key[:])
- crypto.RandBytes(kvEntry.Value[:])
-
- encoded := kvEntry.MarshalMsg(nil)
- require.GreaterOrEqual(t, MaxEncodedKVDataSize, len(encoded))
-
-}
diff --git a/ledger/catchupaccessor.go b/ledger/catchupaccessor.go
index cbe18ba24..41e14a167 100644
--- a/ledger/catchupaccessor.go
+++ b/ledger/catchupaccessor.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -31,6 +31,7 @@ import (
"github.com/algorand/go-algorand/crypto/merkletrie"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
+ "github.com/algorand/go-algorand/ledger/encoded"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store"
"github.com/algorand/go-algorand/ledger/store/blockdb"
@@ -97,7 +98,7 @@ type stagingWriter interface {
writeBalances(context.Context, []store.NormalizedAccountBalance) error
writeCreatables(context.Context, []store.NormalizedAccountBalance) error
writeHashes(context.Context, []store.NormalizedAccountBalance) error
- writeKVs(context.Context, []encodedKVRecordV6) error
+ writeKVs(context.Context, []encoded.KVRecordV6) error
isShared() bool
}
@@ -112,7 +113,7 @@ func (w *stagingWriterImpl) writeBalances(ctx context.Context, balances []store.
})
}
-func (w *stagingWriterImpl) writeKVs(ctx context.Context, kvrs []encodedKVRecordV6) error {
+func (w *stagingWriterImpl) writeKVs(ctx context.Context, kvrs []encoded.KVRecordV6) error {
return w.wdb.Atomic(func(ctx context.Context, tx *sql.Tx) (err error) {
crw := store.NewCatchpointSQLReaderWriter(tx)
@@ -166,6 +167,14 @@ type catchpointCatchupAccessorImpl struct {
nextExpectedAccount basics.Address
}
+// catchpointAccountResourceCounter keeps track of the resources processed for the current account
+type catchpointAccountResourceCounter struct {
+ totalAppParams uint64
+ totalAppLocalStates uint64
+ totalAssetParams uint64
+ totalAssets uint64
+}
+
// CatchpointCatchupState is the state of the current catchpoint catchup process
type CatchpointCatchupState int32
@@ -388,7 +397,7 @@ func (c *catchpointCatchupAccessorImpl) processStagingBalances(ctx context.Conte
var normalizedAccountBalances []store.NormalizedAccountBalance
var expectingMoreEntries []bool
- var chunkKVs []encodedKVRecordV6
+ var chunkKVs []encoded.KVRecordV6
switch progress.Version {
default:
@@ -659,7 +668,7 @@ func (c *catchpointCatchupAccessorImpl) BuildMerkleTrie(ctx context.Context, pro
defer close(writerQueue)
err := rdb.Atomic(func(transactionCtx context.Context, tx *sql.Tx) (err error) {
- it := makeCatchpointPendingHashesIterator(trieRebuildAccountChunkSize, tx)
+ it := store.MakeCatchpointPendingHashesIterator(trieRebuildAccountChunkSize, tx)
var hashes [][]byte
for {
hashes, err = it.Next(transactionCtx)
diff --git a/ledger/catchupaccessor_test.go b/ledger/catchupaccessor_test.go
index d79d636e3..bb4d8aa16 100644
--- a/ledger/catchupaccessor_test.go
+++ b/ledger/catchupaccessor_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -32,6 +32,7 @@ import (
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
+ "github.com/algorand/go-algorand/ledger/encoded"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store"
ledgertesting "github.com/algorand/go-algorand/ledger/testing"
@@ -57,9 +58,9 @@ func createTestingEncodedChunks(accountsCount uint64) (encodedAccountChunks [][]
last64KIndex = len(encodedAccountChunks)
}
var chunk catchpointFileChunkV6
- chunk.Balances = make([]encodedBalanceRecordV6, chunkSize)
+ chunk.Balances = make([]encoded.BalanceRecordV6, chunkSize)
for i := uint64(0); i < chunkSize; i++ {
- var randomAccount encodedBalanceRecordV6
+ var randomAccount encoded.BalanceRecordV6
accountData := store.BaseAccountData{}
accountData.MicroAlgos.Raw = crypto.RandUint63()
randomAccount.AccountData = protocol.Encode(&accountData)
@@ -406,8 +407,8 @@ func TestCatchupAccessorResourceCountMismatch(t *testing.T) {
require.NoError(t, err)
var balances catchpointFileChunkV6
- balances.Balances = make([]encodedBalanceRecordV6, 1)
- var randomAccount encodedBalanceRecordV6
+ balances.Balances = make([]encoded.BalanceRecordV6, 1)
+ var randomAccount encoded.BalanceRecordV6
accountData := store.BaseAccountData{}
accountData.MicroAlgos.Raw = crypto.RandUint63()
accountData.TotalAppParams = 1
@@ -435,7 +436,7 @@ func (w *testStagingWriter) writeCreatables(ctx context.Context, balances []stor
return nil
}
-func (w *testStagingWriter) writeKVs(ctx context.Context, kvrs []encodedKVRecordV6) error {
+func (w *testStagingWriter) writeKVs(ctx context.Context, kvrs []encoded.KVRecordV6) error {
return nil
}
@@ -485,8 +486,8 @@ func TestCatchupAccessorProcessStagingBalances(t *testing.T) {
return accountData
}
- encodedBalanceRecordFromBase := func(addr basics.Address, base store.BaseAccountData, resources map[uint64]msgp.Raw, more bool) encodedBalanceRecordV6 {
- ebr := encodedBalanceRecordV6{
+ encodedBalanceRecordFromBase := func(addr basics.Address, base store.BaseAccountData, resources map[uint64]msgp.Raw, more bool) encoded.BalanceRecordV6 {
+ ebr := encoded.BalanceRecordV6{
Address: addr,
AccountData: protocol.Encode(&base),
Resources: resources,
@@ -530,14 +531,14 @@ func TestCatchupAccessorProcessStagingBalances(t *testing.T) {
// make chunks
chunks := []catchpointFileChunkV6{
{
- Balances: []encodedBalanceRecordV6{
+ Balances: []encoded.BalanceRecordV6{
encodedBalanceRecordFromBase(ledgertesting.RandomAddress(), acctA, nil, false),
encodedBalanceRecordFromBase(ledgertesting.RandomAddress(), acctB, nil, false),
encodedBalanceRecordFromBase(addrX, acctX, acctXRes1, true),
},
},
{
- Balances: []encodedBalanceRecordV6{
+ Balances: []encoded.BalanceRecordV6{
encodedBalanceRecordFromBase(addrX, acctX, acctXRes2, false),
encodedBalanceRecordFromBase(ledgertesting.RandomAddress(), acctC, nil, false),
encodedBalanceRecordFromBase(ledgertesting.RandomAddress(), acctD, nil, false),
diff --git a/ledger/double_test.go b/ledger/double_test.go
index 40e39b88c..9b4ca20f1 100644
--- a/ledger/double_test.go
+++ b/ledger/double_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -152,8 +152,8 @@ func (dl *DoubleLedger) fundedApp(sender basics.Address, amount uint64, source s
}
func (dl *DoubleLedger) reloadLedgers() {
- require.NoError(dl.t, dl.generator.ReloadLedger())
- require.NoError(dl.t, dl.validator.ReloadLedger())
+ require.NoError(dl.t, dl.generator.reloadLedger())
+ require.NoError(dl.t, dl.validator.reloadLedger())
}
func checkBlock(t *testing.T, checkLedger *Ledger, vb *ledgercore.ValidatedBlock) {
diff --git a/ledger/encoded/msgp_gen.go b/ledger/encoded/msgp_gen.go
new file mode 100644
index 000000000..189a6b73b
--- /dev/null
+++ b/ledger/encoded/msgp_gen.go
@@ -0,0 +1,587 @@
+package encoded
+
+// Code generated by github.com/algorand/msgp DO NOT EDIT.
+
+import (
+ "sort"
+
+ "github.com/algorand/msgp/msgp"
+)
+
+// The following msgp objects are implemented in this file:
+// BalanceRecordV5
+// |-----> (*) MarshalMsg
+// |-----> (*) CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> (*) Msgsize
+// |-----> (*) MsgIsZero
+//
+// BalanceRecordV6
+// |-----> (*) MarshalMsg
+// |-----> (*) CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> (*) Msgsize
+// |-----> (*) MsgIsZero
+//
+// KVRecordV6
+// |-----> (*) MarshalMsg
+// |-----> (*) CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> (*) Msgsize
+// |-----> (*) MsgIsZero
+//
+
+// MarshalMsg implements msgp.Marshaler
+func (z *BalanceRecordV5) MarshalMsg(b []byte) (o []byte) {
+ o = msgp.Require(b, z.Msgsize())
+ // omitempty: check for empty values
+ zb0001Len := uint32(2)
+ var zb0001Mask uint8 /* 3 bits */
+ if (*z).AccountData.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x2
+ }
+ if (*z).Address.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x4
+ }
+ // variable map header, size zb0001Len
+ o = append(o, 0x80|uint8(zb0001Len))
+ if zb0001Len != 0 {
+ if (zb0001Mask & 0x2) == 0 { // if not empty
+ // string "ad"
+ o = append(o, 0xa2, 0x61, 0x64)
+ o = (*z).AccountData.MarshalMsg(o)
+ }
+ if (zb0001Mask & 0x4) == 0 { // if not empty
+ // string "pk"
+ o = append(o, 0xa2, 0x70, 0x6b)
+ o = (*z).Address.MarshalMsg(o)
+ }
+ }
+ return
+}
+
+func (_ *BalanceRecordV5) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(*BalanceRecordV5)
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *BalanceRecordV5) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var field []byte
+ _ = field
+ var zb0001 int
+ var zb0002 bool
+ zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).Address.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Address")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).AccountData.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "AccountData")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0001)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0002 {
+ (*z) = BalanceRecordV5{}
+ }
+ for zb0001 > 0 {
+ zb0001--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ switch string(field) {
+ case "pk":
+ bts, err = (*z).Address.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Address")
+ return
+ }
+ case "ad":
+ bts, err = (*z).AccountData.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "AccountData")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ }
+ }
+ }
+ o = bts
+ return
+}
+
+func (_ *BalanceRecordV5) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*BalanceRecordV5)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z *BalanceRecordV5) Msgsize() (s int) {
+ s = 1 + 3 + (*z).Address.Msgsize() + 3 + (*z).AccountData.Msgsize()
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z *BalanceRecordV5) MsgIsZero() bool {
+ return ((*z).Address.MsgIsZero()) && ((*z).AccountData.MsgIsZero())
+}
+
+// MarshalMsg implements msgp.Marshaler
+func (z *BalanceRecordV6) MarshalMsg(b []byte) (o []byte) {
+ o = msgp.Require(b, z.Msgsize())
+ // omitempty: check for empty values
+ zb0003Len := uint32(4)
+ var zb0003Mask uint8 /* 5 bits */
+ if (*z).Address.MsgIsZero() {
+ zb0003Len--
+ zb0003Mask |= 0x2
+ }
+ if (*z).AccountData.MsgIsZero() {
+ zb0003Len--
+ zb0003Mask |= 0x4
+ }
+ if len((*z).Resources) == 0 {
+ zb0003Len--
+ zb0003Mask |= 0x8
+ }
+ if (*z).ExpectingMoreEntries == false {
+ zb0003Len--
+ zb0003Mask |= 0x10
+ }
+ // variable map header, size zb0003Len
+ o = append(o, 0x80|uint8(zb0003Len))
+ if zb0003Len != 0 {
+ if (zb0003Mask & 0x2) == 0 { // if not empty
+ // string "a"
+ o = append(o, 0xa1, 0x61)
+ o = (*z).Address.MarshalMsg(o)
+ }
+ if (zb0003Mask & 0x4) == 0 { // if not empty
+ // string "b"
+ o = append(o, 0xa1, 0x62)
+ o = (*z).AccountData.MarshalMsg(o)
+ }
+ if (zb0003Mask & 0x8) == 0 { // if not empty
+ // string "c"
+ o = append(o, 0xa1, 0x63)
+ if (*z).Resources == nil {
+ o = msgp.AppendNil(o)
+ } else {
+ o = msgp.AppendMapHeader(o, uint32(len((*z).Resources)))
+ }
+ zb0001_keys := make([]uint64, 0, len((*z).Resources))
+ for zb0001 := range (*z).Resources {
+ zb0001_keys = append(zb0001_keys, zb0001)
+ }
+ sort.Sort(SortUint64(zb0001_keys))
+ for _, zb0001 := range zb0001_keys {
+ zb0002 := (*z).Resources[zb0001]
+ _ = zb0002
+ o = msgp.AppendUint64(o, zb0001)
+ o = zb0002.MarshalMsg(o)
+ }
+ }
+ if (zb0003Mask & 0x10) == 0 { // if not empty
+ // string "e"
+ o = append(o, 0xa1, 0x65)
+ o = msgp.AppendBool(o, (*z).ExpectingMoreEntries)
+ }
+ }
+ return
+}
+
+func (_ *BalanceRecordV6) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(*BalanceRecordV6)
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *BalanceRecordV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var field []byte
+ _ = field
+ var zb0003 int
+ var zb0004 bool
+ zb0003, zb0004, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0003, zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0003 > 0 {
+ zb0003--
+ bts, err = (*z).Address.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Address")
+ return
+ }
+ }
+ if zb0003 > 0 {
+ zb0003--
+ bts, err = (*z).AccountData.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "AccountData")
+ return
+ }
+ }
+ if zb0003 > 0 {
+ zb0003--
+ var zb0005 int
+ var zb0006 bool
+ zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Resources")
+ return
+ }
+ if zb0005 > resourcesPerCatchpointFileChunkBackwardCompatible {
+ err = msgp.ErrOverflow(uint64(zb0005), uint64(resourcesPerCatchpointFileChunkBackwardCompatible))
+ err = msgp.WrapError(err, "struct-from-array", "Resources")
+ return
+ }
+ if zb0006 {
+ (*z).Resources = nil
+ } else if (*z).Resources == nil {
+ (*z).Resources = make(map[uint64]msgp.Raw, zb0005)
+ }
+ for zb0005 > 0 {
+ var zb0001 uint64
+ var zb0002 msgp.Raw
+ zb0005--
+ zb0001, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Resources")
+ return
+ }
+ bts, err = zb0002.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Resources", zb0001)
+ return
+ }
+ (*z).Resources[zb0001] = zb0002
+ }
+ }
+ if zb0003 > 0 {
+ zb0003--
+ (*z).ExpectingMoreEntries, bts, err = msgp.ReadBoolBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "ExpectingMoreEntries")
+ return
+ }
+ }
+ if zb0003 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0003)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0004 {
+ (*z) = BalanceRecordV6{}
+ }
+ for zb0003 > 0 {
+ zb0003--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ switch string(field) {
+ case "a":
+ bts, err = (*z).Address.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Address")
+ return
+ }
+ case "b":
+ bts, err = (*z).AccountData.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "AccountData")
+ return
+ }
+ case "c":
+ var zb0007 int
+ var zb0008 bool
+ zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Resources")
+ return
+ }
+ if zb0007 > resourcesPerCatchpointFileChunkBackwardCompatible {
+ err = msgp.ErrOverflow(uint64(zb0007), uint64(resourcesPerCatchpointFileChunkBackwardCompatible))
+ err = msgp.WrapError(err, "Resources")
+ return
+ }
+ if zb0008 {
+ (*z).Resources = nil
+ } else if (*z).Resources == nil {
+ (*z).Resources = make(map[uint64]msgp.Raw, zb0007)
+ }
+ for zb0007 > 0 {
+ var zb0001 uint64
+ var zb0002 msgp.Raw
+ zb0007--
+ zb0001, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Resources")
+ return
+ }
+ bts, err = zb0002.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Resources", zb0001)
+ return
+ }
+ (*z).Resources[zb0001] = zb0002
+ }
+ case "e":
+ (*z).ExpectingMoreEntries, bts, err = msgp.ReadBoolBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "ExpectingMoreEntries")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ }
+ }
+ }
+ o = bts
+ return
+}
+
+func (_ *BalanceRecordV6) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*BalanceRecordV6)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z *BalanceRecordV6) Msgsize() (s int) {
+ s = 1 + 2 + (*z).Address.Msgsize() + 2 + (*z).AccountData.Msgsize() + 2 + msgp.MapHeaderSize
+ if (*z).Resources != nil {
+ for zb0001, zb0002 := range (*z).Resources {
+ _ = zb0001
+ _ = zb0002
+ s += 0 + msgp.Uint64Size + zb0002.Msgsize()
+ }
+ }
+ s += 2 + msgp.BoolSize
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z *BalanceRecordV6) MsgIsZero() bool {
+ return ((*z).Address.MsgIsZero()) && ((*z).AccountData.MsgIsZero()) && (len((*z).Resources) == 0) && ((*z).ExpectingMoreEntries == false)
+}
+
+// MarshalMsg implements msgp.Marshaler
+func (z *KVRecordV6) MarshalMsg(b []byte) (o []byte) {
+ o = msgp.Require(b, z.Msgsize())
+ // omitempty: check for empty values
+ zb0001Len := uint32(2)
+ var zb0001Mask uint8 /* 3 bits */
+ if len((*z).Key) == 0 {
+ zb0001Len--
+ zb0001Mask |= 0x2
+ }
+ if len((*z).Value) == 0 {
+ zb0001Len--
+ zb0001Mask |= 0x4
+ }
+ // variable map header, size zb0001Len
+ o = append(o, 0x80|uint8(zb0001Len))
+ if zb0001Len != 0 {
+ if (zb0001Mask & 0x2) == 0 { // if not empty
+ // string "k"
+ o = append(o, 0xa1, 0x6b)
+ o = msgp.AppendBytes(o, (*z).Key)
+ }
+ if (zb0001Mask & 0x4) == 0 { // if not empty
+ // string "v"
+ o = append(o, 0xa1, 0x76)
+ o = msgp.AppendBytes(o, (*z).Value)
+ }
+ }
+ return
+}
+
+func (_ *KVRecordV6) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(*KVRecordV6)
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *KVRecordV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var field []byte
+ _ = field
+ var zb0001 int
+ var zb0002 bool
+ zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0001 > 0 {
+ zb0001--
+ var zb0003 int
+ zb0003, err = msgp.ReadBytesBytesHeader(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Key")
+ return
+ }
+ if zb0003 > KVRecordV6MaxKeyLength {
+ err = msgp.ErrOverflow(uint64(zb0003), uint64(KVRecordV6MaxKeyLength))
+ return
+ }
+ (*z).Key, bts, err = msgp.ReadBytesBytes(bts, (*z).Key)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Key")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ var zb0004 int
+ zb0004, err = msgp.ReadBytesBytesHeader(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Value")
+ return
+ }
+ if zb0004 > KVRecordV6MaxValueLength {
+ err = msgp.ErrOverflow(uint64(zb0004), uint64(KVRecordV6MaxValueLength))
+ return
+ }
+ (*z).Value, bts, err = msgp.ReadBytesBytes(bts, (*z).Value)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Value")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0001)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0002 {
+ (*z) = KVRecordV6{}
+ }
+ for zb0001 > 0 {
+ zb0001--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ switch string(field) {
+ case "k":
+ var zb0005 int
+ zb0005, err = msgp.ReadBytesBytesHeader(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Key")
+ return
+ }
+ if zb0005 > KVRecordV6MaxKeyLength {
+ err = msgp.ErrOverflow(uint64(zb0005), uint64(KVRecordV6MaxKeyLength))
+ return
+ }
+ (*z).Key, bts, err = msgp.ReadBytesBytes(bts, (*z).Key)
+ if err != nil {
+ err = msgp.WrapError(err, "Key")
+ return
+ }
+ case "v":
+ var zb0006 int
+ zb0006, err = msgp.ReadBytesBytesHeader(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Value")
+ return
+ }
+ if zb0006 > KVRecordV6MaxValueLength {
+ err = msgp.ErrOverflow(uint64(zb0006), uint64(KVRecordV6MaxValueLength))
+ return
+ }
+ (*z).Value, bts, err = msgp.ReadBytesBytes(bts, (*z).Value)
+ if err != nil {
+ err = msgp.WrapError(err, "Value")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ }
+ }
+ }
+ o = bts
+ return
+}
+
+func (_ *KVRecordV6) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*KVRecordV6)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z *KVRecordV6) Msgsize() (s int) {
+ s = 1 + 2 + msgp.BytesPrefixSize + len((*z).Key) + 2 + msgp.BytesPrefixSize + len((*z).Value)
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z *KVRecordV6) MsgIsZero() bool {
+ return (len((*z).Key) == 0) && (len((*z).Value) == 0)
+}
diff --git a/ledger/encoded/msgp_gen_test.go b/ledger/encoded/msgp_gen_test.go
new file mode 100644
index 000000000..415339c72
--- /dev/null
+++ b/ledger/encoded/msgp_gen_test.go
@@ -0,0 +1,195 @@
+//go:build !skip_msgp_testing
+// +build !skip_msgp_testing
+
+package encoded
+
+// Code generated by github.com/algorand/msgp DO NOT EDIT.
+
+import (
+ "testing"
+
+ "github.com/algorand/msgp/msgp"
+
+ "github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/test/partitiontest"
+)
+
+func TestMarshalUnmarshalBalanceRecordV5(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ v := BalanceRecordV5{}
+ bts := v.MarshalMsg(nil)
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingBalanceRecordV5(t *testing.T) {
+ protocol.RunEncodingTest(t, &BalanceRecordV5{})
+}
+
+func BenchmarkMarshalMsgBalanceRecordV5(b *testing.B) {
+ v := BalanceRecordV5{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgBalanceRecordV5(b *testing.B) {
+ v := BalanceRecordV5{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalBalanceRecordV5(b *testing.B) {
+ v := BalanceRecordV5{}
+ bts := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestMarshalUnmarshalBalanceRecordV6(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ v := BalanceRecordV6{}
+ bts := v.MarshalMsg(nil)
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingBalanceRecordV6(t *testing.T) {
+ protocol.RunEncodingTest(t, &BalanceRecordV6{})
+}
+
+func BenchmarkMarshalMsgBalanceRecordV6(b *testing.B) {
+ v := BalanceRecordV6{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgBalanceRecordV6(b *testing.B) {
+ v := BalanceRecordV6{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalBalanceRecordV6(b *testing.B) {
+ v := BalanceRecordV6{}
+ bts := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestMarshalUnmarshalKVRecordV6(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ v := KVRecordV6{}
+ bts := v.MarshalMsg(nil)
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingKVRecordV6(t *testing.T) {
+ protocol.RunEncodingTest(t, &KVRecordV6{})
+}
+
+func BenchmarkMarshalMsgKVRecordV6(b *testing.B) {
+ v := KVRecordV6{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgKVRecordV6(b *testing.B) {
+ v := KVRecordV6{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalKVRecordV6(b *testing.B) {
+ v := KVRecordV6{}
+ bts := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/node/poolStats.go b/ledger/encoded/recordsV5.go
index 19b0090b4..9af95207c 100644
--- a/node/poolStats.go
+++ b/ledger/encoded/recordsV5.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -14,11 +14,17 @@
// 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 node
+package encoded
-// PoolStats represents some statistics about the transaction pool
-type PoolStats struct {
- NumConfirmed uint64
- NumOutstanding uint64
- NumExpired uint64
+import (
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/msgp/msgp"
+)
+
+// BalanceRecordV5 is the encoded account balance record.
+type BalanceRecordV5 struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+
+ Address basics.Address `codec:"pk,allocbound=crypto.DigestSize"`
+ AccountData msgp.Raw `codec:"ad"` // encoding of basics.AccountData
}
diff --git a/ledger/encoded/recordsV6.go b/ledger/encoded/recordsV6.go
new file mode 100644
index 000000000..6d0616c80
--- /dev/null
+++ b/ledger/encoded/recordsV6.go
@@ -0,0 +1,64 @@
+// 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 encoded
+
+import (
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/msgp/msgp"
+)
+
+// Adjust these to be big enough for boxes, but not directly tied to box values.
+const (
+ // For boxes: "bx:<8 bytes><64 byte name>"
+ KVRecordV6MaxKeyLength = 128
+
+ // For boxes: MaxBoxSize
+ KVRecordV6MaxValueLength = 32768
+
+ // MaxEncodedKVDataSize is the max size of serialized KV entry, checked with TestEncodedKVDataSize.
+ // Exact value is 32906 that is 10 bytes more than 32768 + 128
+ MaxEncodedKVDataSize = 33000
+
+ // resourcesPerCatchpointFileChunkBackwardCompatible is the old value for ResourcesPerCatchpointFileChunk.
+ // Size of a single resource entry was underestimated to 300 bytes that holds only for assets and not for apps.
+ // It is safe to remove after April, 2023 since we are only supporting catchpoint that are 6 months old.
+ resourcesPerCatchpointFileChunkBackwardCompatible = 300_000
+)
+
+// SortUint64 re-export this sort, which is implemented in basics, and being used by the msgp when
+// encoding the resources map below.
+type SortUint64 = basics.SortUint64
+
+// BalanceRecordV6 is the encoded account balance record.
+type BalanceRecordV6 struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+
+ Address basics.Address `codec:"a,allocbound=crypto.DigestSize"`
+ AccountData msgp.Raw `codec:"b"` // encoding of baseAccountData
+ Resources map[uint64]msgp.Raw `codec:"c,allocbound=resourcesPerCatchpointFileChunkBackwardCompatible"` // map of resourcesData
+
+ // flag indicating whether there are more records for the same account coming up
+ ExpectingMoreEntries bool `codec:"e"`
+}
+
+// KVRecordV6 is the encoded KV record.
+type KVRecordV6 struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+
+ Key []byte `codec:"k,allocbound=KVRecordV6MaxKeyLength"`
+ Value []byte `codec:"v,allocbound=KVRecordV6MaxValueLength"`
+}
diff --git a/ledger/encoded/recordsV6_test.go b/ledger/encoded/recordsV6_test.go
new file mode 100644
index 000000000..9e5dd1438
--- /dev/null
+++ b/ledger/encoded/recordsV6_test.go
@@ -0,0 +1,63 @@
+// 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 encoded
+
+import (
+ "math"
+ "testing"
+
+ "github.com/algorand/avm-abi/apps"
+ "github.com/algorand/go-algorand/config"
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/test/partitiontest"
+ "github.com/stretchr/testify/require"
+)
+
+func TestEncodedKVRecordV6Allocbounds(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ for version, params := range config.Consensus {
+ require.GreaterOrEqualf(t, uint64(KVRecordV6MaxValueLength), params.MaxBoxSize, "Allocbound constant no longer valid as of consensus version %s", version)
+ longestPossibleBoxName := string(make([]byte, params.MaxAppKeyLen))
+ longestPossibleKey := apps.MakeBoxKey(math.MaxUint64, longestPossibleBoxName)
+ require.GreaterOrEqualf(t, KVRecordV6MaxValueLength, len(longestPossibleKey), "Allocbound constant no longer valid as of consensus version %s", version)
+ }
+}
+
+func TestEncodedKVDataSize(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ currentConsensusParams := config.Consensus[protocol.ConsensusCurrentVersion]
+
+ require.GreaterOrEqual(t, KVRecordV6MaxKeyLength, currentConsensusParams.MaxAppKeyLen)
+ require.GreaterOrEqual(t, uint64(KVRecordV6MaxValueLength), currentConsensusParams.MaxBoxSize)
+
+ kvEntry := KVRecordV6{
+ Key: make([]byte, KVRecordV6MaxKeyLength),
+ Value: make([]byte, KVRecordV6MaxValueLength),
+ }
+
+ crypto.RandBytes(kvEntry.Key[:])
+ crypto.RandBytes(kvEntry.Value[:])
+
+ encoded := kvEntry.MarshalMsg(nil)
+ require.GreaterOrEqual(t, MaxEncodedKVDataSize, len(encoded))
+
+}
diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go
index 802e8de9d..4a2317dc1 100644
--- a/ledger/eval_simple_test.go
+++ b/ledger/eval_simple_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/evalbench_test.go b/ledger/evalbench_test.go
index fcbfef681..9b827d83f 100644
--- a/ledger/evalbench_test.go
+++ b/ledger/evalbench_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/evalindexer.go b/ledger/evalindexer.go
index 3e2d8ca34..a575c638b 100644
--- a/ledger/evalindexer.go
+++ b/ledger/evalindexer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/evalindexer_test.go b/ledger/evalindexer_test.go
index 77b7dbde7..d5636f27c 100644
--- a/ledger/evalindexer_test.go
+++ b/ledger/evalindexer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/fullblock_perf_test.go b/ledger/fullblock_perf_test.go
index d2d50cd8f..9bea4a00a 100644
--- a/ledger/fullblock_perf_test.go
+++ b/ledger/fullblock_perf_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -32,6 +32,7 @@ import (
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
@@ -81,7 +82,7 @@ func setupEnv(b *testing.B, numAccts int) (bc *benchConfig) {
creator := basics.Address{}
_, err := rand.Read(creator[:])
require.NoError(b, err)
- genesisInitState.Accounts[creator] = basics.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890000000000})
+ genesisInitState.Accounts[creator] = basics_testing.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890000000000})
logger := logging.TestingLog(b)
logger.SetLevel(logging.Warn)
diff --git a/ledger/internal/appcow.go b/ledger/internal/appcow.go
index d38fbfacc..c43eb94f1 100644
--- a/ledger/internal/appcow.go
+++ b/ledger/internal/appcow.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/appcow_test.go b/ledger/internal/appcow_test.go
index db659636b..d4025524c 100644
--- a/ledger/internal/appcow_test.go
+++ b/ledger/internal/appcow_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/applications.go b/ledger/internal/applications.go
index 1bf694eed..f0466b7d4 100644
--- a/ledger/internal/applications.go
+++ b/ledger/internal/applications.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -19,6 +19,7 @@ package internal
import (
"fmt"
+ "github.com/algorand/avm-abi/apps"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions/logic"
@@ -222,7 +223,7 @@ func (cs *roundCowState) NewBox(appIdx basics.AppIndex, key string, value []byte
return fmt.Errorf("box size too large: %d, maximum is %d", size, cs.proto.MaxBoxSize)
}
- fullKey := logic.MakeBoxKey(appIdx, key)
+ fullKey := apps.MakeBoxKey(uint64(appIdx), key)
_, exists, err := cs.kvGet(fullKey)
if err != nil {
return err
@@ -246,12 +247,12 @@ func (cs *roundCowState) NewBox(appIdx basics.AppIndex, key string, value []byte
}
func (cs *roundCowState) GetBox(appIdx basics.AppIndex, key string) ([]byte, bool, error) {
- fullKey := logic.MakeBoxKey(appIdx, key)
+ fullKey := apps.MakeBoxKey(uint64(appIdx), key)
return cs.kvGet(fullKey)
}
func (cs *roundCowState) SetBox(appIdx basics.AppIndex, key string, value []byte) error {
- fullKey := logic.MakeBoxKey(appIdx, key)
+ fullKey := apps.MakeBoxKey(uint64(appIdx), key)
old, ok, err := cs.kvGet(fullKey)
if err != nil {
return err
@@ -267,7 +268,7 @@ func (cs *roundCowState) SetBox(appIdx basics.AppIndex, key string, value []byte
}
func (cs *roundCowState) DelBox(appIdx basics.AppIndex, key string, appAddr basics.Address) (bool, error) {
- fullKey := logic.MakeBoxKey(appIdx, key)
+ fullKey := apps.MakeBoxKey(uint64(appIdx), key)
value, ok, err := cs.kvGet(fullKey)
if err != nil {
diff --git a/ledger/internal/assetcow.go b/ledger/internal/assetcow.go
index f675d823c..3813dad7c 100644
--- a/ledger/internal/assetcow.go
+++ b/ledger/internal/assetcow.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/cow.go b/ledger/internal/cow.go
index e27dd777f..ade61f820 100644
--- a/ledger/internal/cow.go
+++ b/ledger/internal/cow.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/cow_creatables.go b/ledger/internal/cow_creatables.go
index 9db697742..d43135cf8 100644
--- a/ledger/internal/cow_creatables.go
+++ b/ledger/internal/cow_creatables.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/cow_test.go b/ledger/internal/cow_test.go
index 14eb748fb..562c60f92 100644
--- a/ledger/internal/cow_test.go
+++ b/ledger/internal/cow_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/eval.go b/ledger/internal/eval.go
index 5fc3c51fa..1a1a1eea5 100644
--- a/ledger/internal/eval.go
+++ b/ledger/internal/eval.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -594,6 +594,8 @@ type BlockEvaluator struct {
l LedgerForEvaluator
maxTxnBytesPerBlock int
+
+ Tracer logic.EvalTracer
}
// LedgerForEvaluator defines the ledger interface needed by the evaluator.
@@ -925,14 +927,7 @@ func (eval *BlockEvaluator) Transaction(txn transactions.SignedTxn, ad transacti
// TransactionGroup tentatively adds a new transaction group as part of this block evaluation.
// If the transaction group cannot be added to the block without violating some constraints,
// an error is returned and the block evaluator state is unchanged.
-func (eval *BlockEvaluator) TransactionGroup(txads []transactions.SignedTxnWithAD) error {
- return eval.transactionGroup(txads)
-}
-
-// transactionGroup tentatively executes a group of transactions as part of this block evaluation.
-// If the transaction group cannot be added to the block without violating some constraints,
-// an error is returned and the block evaluator state is unchanged.
-func (eval *BlockEvaluator) transactionGroup(txgroup []transactions.SignedTxnWithAD) error {
+func (eval *BlockEvaluator) TransactionGroup(txgroup []transactions.SignedTxnWithAD) error {
// Nothing to do if there are no transactions.
if len(txgroup) == 0 {
return nil
@@ -953,17 +948,30 @@ func (eval *BlockEvaluator) transactionGroup(txgroup []transactions.SignedTxnWit
defer cow.recycle()
evalParams := logic.NewEvalParams(txgroup, &eval.proto, &eval.specials)
+ evalParams.Tracer = eval.Tracer
+
+ if eval.Tracer != nil {
+ eval.Tracer.BeforeTxnGroup(evalParams)
+ }
// Evaluate each transaction in the group
txibs = make([]transactions.SignedTxnInBlock, 0, len(txgroup))
for gi, txad := range txgroup {
var txib transactions.SignedTxnInBlock
+ if eval.Tracer != nil {
+ eval.Tracer.BeforeTxn(evalParams, gi)
+ }
+
err := eval.transaction(txad.SignedTxn, evalParams, gi, txad.ApplyData, cow, &txib)
if err != nil {
return err
}
+ if eval.Tracer != nil {
+ eval.Tracer.AfterTxn(evalParams, gi, txib.ApplyData)
+ }
+
txibs = append(txibs, txib)
if eval.validate {
@@ -1010,6 +1018,10 @@ func (eval *BlockEvaluator) transactionGroup(txgroup []transactions.SignedTxnWit
eval.blockTxBytes += groupTxBytes
cow.commitToParent()
+ if eval.Tracer != nil {
+ eval.Tracer.AfterTxnGroup(evalParams)
+ }
+
return nil
}
diff --git a/ledger/internal/eval_test.go b/ledger/internal/eval_test.go
index d04294e4c..5803a4d61 100644
--- a/ledger/internal/eval_test.go
+++ b/ledger/internal/eval_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -33,11 +33,14 @@ import (
"github.com/algorand/go-algorand/crypto/merklesignature"
"github.com/algorand/go-algorand/crypto/stateproof"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/stateproofmsg"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
+ "github.com/algorand/go-algorand/data/transactions/logic/mocktracer"
"github.com/algorand/go-algorand/data/transactions/verify"
+ "github.com/algorand/go-algorand/data/txntest"
"github.com/algorand/go-algorand/ledger/apply"
"github.com/algorand/go-algorand/ledger/ledgercore"
ledgertesting "github.com/algorand/go-algorand/ledger/testing"
@@ -307,8 +310,203 @@ func TestPrivateTransactionGroup(t *testing.T) {
require.Error(t, err) // too many
}
+func tealOpLogs(count int) []mocktracer.Event {
+ var log []mocktracer.Event
+
+ for i := 0; i < count; i++ {
+ log = append(log, mocktracer.BeforeOpcode(), mocktracer.AfterOpcode())
+ }
+
+ return log
+}
+
+func flatten(rows [][]mocktracer.Event) []mocktracer.Event {
+ var out []mocktracer.Event
+ for _, row := range rows {
+ out = append(out, row...)
+ }
+ return out
+}
+
+const innerTxnTestProgram string = `#pragma version 6
+itxn_begin
+int appl
+itxn_field TypeEnum
+int NoOp
+itxn_field OnCompletion
+byte 0x068101 // #pragma version 6; int 1;
+dup
+itxn_field ApprovalProgram
+itxn_field ClearStateProgram
+itxn_submit
+
+itxn_begin
+int pay
+itxn_field TypeEnum
+int 1
+itxn_field Amount
+global CurrentApplicationAddress
+itxn_field Receiver
+itxn_next
+int pay
+itxn_field TypeEnum
+int 2
+itxn_field Amount
+global CurrentApplicationAddress
+itxn_field Receiver
+itxn_submit
+
+int 1
+`
+
+func TestTransactionGroupWithTracer(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ genesisInitState, addrs, keys := ledgertesting.Genesis(10)
+
+ innerAppID := 3
+ innerAppAddress := basics.AppIndex(innerAppID).Address()
+ balances := genesisInitState.Accounts
+ balances[innerAppAddress] = basics_testing.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1000000})
+
+ genesisBalances := bookkeeping.GenesisBalances{
+ Balances: genesisInitState.Accounts,
+ FeeSink: testSinkAddr,
+ RewardsPool: testPoolAddr,
+ Timestamp: 0,
+ }
+ l := newTestLedger(t, genesisBalances)
+
+ blkHeader, err := l.BlockHdr(basics.Round(0))
+ require.NoError(t, err)
+ newBlock := bookkeeping.MakeBlock(blkHeader)
+ eval, err := l.StartEvaluator(newBlock.BlockHeader, 0, 0)
+ require.NoError(t, err)
+ eval.validate = true
+ eval.generate = true
+
+ basicProgram := `#pragma version 6
+byte "hello"
+log
+int 1`
+
+ genHash := l.GenesisHash()
+
+ // a basic app call
+ basicAppCallTxn := txntest.Txn{
+ Type: protocol.ApplicationCallTx,
+ Sender: addrs[0],
+ ApprovalProgram: basicProgram,
+ ClearStateProgram: basicProgram,
+
+ FirstValid: newBlock.Round(),
+ LastValid: newBlock.Round() + 1000,
+ Fee: minFee,
+ GenesisHash: genHash,
+ }
+
+ // a non-app call txn
+ payTxn := txntest.Txn{
+ Type: protocol.PaymentTx,
+ Sender: addrs[1],
+ Receiver: addrs[2],
+ CloseRemainderTo: addrs[3],
+ Amount: 1_000_000,
+
+ FirstValid: newBlock.Round(),
+ LastValid: newBlock.Round() + 1000,
+ Fee: minFee,
+ GenesisHash: genHash,
+ }
+
+ // an app call that spawns inner txns
+ innerAppCallTxn := txntest.Txn{
+ Type: protocol.ApplicationCallTx,
+ Sender: addrs[0],
+ ApprovalProgram: innerTxnTestProgram,
+ ClearStateProgram: basicProgram,
+
+ FirstValid: newBlock.Round(),
+ LastValid: newBlock.Round() + 1000,
+ Fee: minFee,
+ GenesisHash: genHash,
+ }
+
+ txntest.Group(&basicAppCallTxn, &payTxn, &innerAppCallTxn)
+
+ txgroup := transactions.WrapSignedTxnsWithAD([]transactions.SignedTxn{
+ basicAppCallTxn.Txn().Sign(keys[0]),
+ payTxn.Txn().Sign(keys[1]),
+ innerAppCallTxn.Txn().Sign(keys[0]),
+ })
+
+ require.Len(t, eval.block.Payset, 0)
+
+ tracer := &mocktracer.Tracer{}
+ eval.Tracer = tracer
+ err = eval.TransactionGroup(txgroup)
+ require.NoError(t, err)
+
+ require.Len(t, eval.block.Payset, len(txgroup))
+
+ expectedADs := make([]transactions.ApplyData, len(txgroup))
+ for i, txn := range eval.block.Payset {
+ expectedADs[i] = txn.ApplyData
+ }
+
+ expectedEvents := flatten([][]mocktracer.Event{
+ {
+ mocktracer.BeforeTxnGroup(3),
+ mocktracer.BeforeTxn(protocol.ApplicationCallTx), // start basicAppCallTxn
+ mocktracer.BeforeProgram(logic.ModeApp),
+ },
+ tealOpLogs(3),
+ {
+ mocktracer.AfterProgram(logic.ModeApp),
+ mocktracer.AfterTxn(protocol.ApplicationCallTx, expectedADs[0]), // end basicAppCallTxn
+ mocktracer.BeforeTxn(protocol.PaymentTx), // start payTxn
+ mocktracer.AfterTxn(protocol.PaymentTx, expectedADs[1]), // end payTxn
+ mocktracer.BeforeTxn(protocol.ApplicationCallTx), // start innerAppCallTxn
+ mocktracer.BeforeProgram(logic.ModeApp),
+ },
+ tealOpLogs(10),
+ {
+ mocktracer.BeforeOpcode(),
+ mocktracer.BeforeTxnGroup(1), // start first itxn group
+ mocktracer.BeforeTxn(protocol.ApplicationCallTx),
+ mocktracer.BeforeProgram(logic.ModeApp),
+ },
+ tealOpLogs(1),
+ {
+ mocktracer.AfterProgram(logic.ModeApp),
+ mocktracer.AfterTxn(protocol.ApplicationCallTx, expectedADs[2].EvalDelta.InnerTxns[0].ApplyData),
+ mocktracer.AfterTxnGroup(1), // end first itxn group
+ mocktracer.AfterOpcode(),
+ },
+ tealOpLogs(14),
+ {
+ mocktracer.BeforeOpcode(),
+ mocktracer.BeforeTxnGroup(2), // start second itxn group
+ mocktracer.BeforeTxn(protocol.PaymentTx),
+ mocktracer.AfterTxn(protocol.PaymentTx, expectedADs[2].EvalDelta.InnerTxns[1].ApplyData),
+ mocktracer.BeforeTxn(protocol.PaymentTx),
+ mocktracer.AfterTxn(protocol.PaymentTx, expectedADs[2].EvalDelta.InnerTxns[2].ApplyData),
+ mocktracer.AfterTxnGroup(2), // end second itxn group
+ mocktracer.AfterOpcode(),
+ },
+ tealOpLogs(1),
+ {
+ mocktracer.AfterProgram(logic.ModeApp),
+ mocktracer.AfterTxn(protocol.ApplicationCallTx, expectedADs[2]), // end innerAppCallTxn
+ mocktracer.AfterTxnGroup(3),
+ },
+ })
+ require.Equal(t, expectedEvents, tracer.Events)
+}
+
// BlockEvaluator.workaroundOverspentRewards() fixed a couple issues on testnet.
-// This is now part of history and has to be re-created when running catchup on testnet. So, test to ensure it keeps happenning.
+// This is now part of history and has to be re-created when running catchup on testnet. So, test to ensure it keeps happening.
func TestTestnetFixup(t *testing.T) {
partitiontest.PartitionTest(t)
diff --git a/ledger/internal/evalindexer.go b/ledger/internal/evalindexer.go
index b765e0e1e..babf6c87d 100644
--- a/ledger/internal/evalindexer.go
+++ b/ledger/internal/evalindexer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/prefetcher/error.go b/ledger/internal/prefetcher/error.go
index 58b52f891..77c1cb999 100644
--- a/ledger/internal/prefetcher/error.go
+++ b/ledger/internal/prefetcher/error.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/prefetcher/prefetcher.go b/ledger/internal/prefetcher/prefetcher.go
index 07b95c57d..e00d78f70 100644
--- a/ledger/internal/prefetcher/prefetcher.go
+++ b/ledger/internal/prefetcher/prefetcher.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/prefetcher/prefetcher_alignment_test.go b/ledger/internal/prefetcher/prefetcher_alignment_test.go
index 2b553c974..1d5291cf1 100644
--- a/ledger/internal/prefetcher/prefetcher_alignment_test.go
+++ b/ledger/internal/prefetcher/prefetcher_alignment_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/prefetcher/prefetcher_test.go b/ledger/internal/prefetcher/prefetcher_test.go
index 555cc8f6d..b9c1d80eb 100644
--- a/ledger/internal/prefetcher/prefetcher_test.go
+++ b/ledger/internal/prefetcher/prefetcher_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/internal/prefetcher/prefetcher_whitebox_test.go b/ledger/internal/prefetcher/prefetcher_whitebox_test.go
index c5ada063a..6a8738b48 100644
--- a/ledger/internal/prefetcher/prefetcher_whitebox_test.go
+++ b/ledger/internal/prefetcher/prefetcher_whitebox_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledger.go b/ledger/ledger.go
index 12c4148cf..cd023e502 100644
--- a/ledger/ledger.go
+++ b/ledger/ledger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -160,6 +160,11 @@ func OpenLedger(
l.genesisAccounts = make(map[basics.Address]basics.AccountData)
}
+ l.blockQ, err = newBlockQueue(l)
+ if err != nil {
+ return nil, err
+ }
+
err = l.reloadLedger()
if err != nil {
return nil, err
@@ -168,19 +173,12 @@ func OpenLedger(
return l, nil
}
-// ReloadLedger is exported for the benefit of tests in the internal
-// package. Revisit this when we rename / restructure that thing
-func (l *Ledger) ReloadLedger() error {
- return l.reloadLedger()
-}
-
func (l *Ledger) reloadLedger() error {
// similar to the Close function, we want to start by closing the blockQ first. The
// blockQ is having a sync goroutine which indirectly calls other trackers. We want to eliminate that go-routine first,
// and follow up by taking the trackers lock.
if l.blockQ != nil {
- l.blockQ.close()
- l.blockQ = nil
+ l.blockQ.stop()
}
// take the trackers lock. This would ensure that no other goroutine is using the trackers.
@@ -192,9 +190,9 @@ func (l *Ledger) reloadLedger() error {
// init block queue
var err error
- l.blockQ, err = bqInit(l)
+ err = l.blockQ.start()
if err != nil {
- err = fmt.Errorf("reloadLedger.bqInit %v", err)
+ err = fmt.Errorf("reloadLedger.blockQ.start %v", err)
return err
}
@@ -381,8 +379,7 @@ func (l *Ledger) Close() {
// we shut the the blockqueue first, since it's sync goroutine dispatches calls
// back to the trackers.
if l.blockQ != nil {
- l.blockQ.close()
- l.blockQ = nil
+ l.blockQ.stop()
}
// take the trackers lock. This would ensure that no other goroutine is using the trackers.
@@ -399,7 +396,7 @@ func (l *Ledger) Close() {
// RegisterBlockListeners registers listeners that will be called when a
// new block is added to the ledger.
-func (l *Ledger) RegisterBlockListeners(listeners []BlockListener) {
+func (l *Ledger) RegisterBlockListeners(listeners []ledgercore.BlockListener) {
l.notifier.register(listeners)
}
diff --git a/ledger/ledger_perf_test.go b/ledger/ledger_perf_test.go
index a855f70de..2c5cb8ec5 100644
--- a/ledger/ledger_perf_test.go
+++ b/ledger/ledger_perf_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -31,6 +31,7 @@ import (
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
@@ -151,7 +152,7 @@ func benchmarkFullBlocks(params testParams, b *testing.B) {
creator := basics.Address{}
_, err := rand.Read(creator[:])
require.NoError(b, err)
- genesisInitState.Accounts[creator] = basics.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890})
+ genesisInitState.Accounts[creator] = basics_testing.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890})
// Make some accounts to opt into ASA
var accts []basics.Address
@@ -160,7 +161,7 @@ func benchmarkFullBlocks(params testParams, b *testing.B) {
acct := basics.Address{}
_, err = rand.Read(acct[:])
require.NoError(b, err)
- genesisInitState.Accounts[acct] = basics.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890})
+ genesisInitState.Accounts[acct] = basics_testing.MakeAccountData(basics.Offline, basics.MicroAlgos{Raw: 1234567890})
accts = append(accts, acct)
}
}
diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go
index 785281ead..b9a1c6403 100644
--- a/ledger/ledger_test.go
+++ b/ledger/ledger_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -27,6 +27,7 @@ import (
"runtime"
"sort"
"testing"
+ "time"
"github.com/stretchr/testify/require"
@@ -1477,6 +1478,31 @@ func benchLedgerCache(b *testing.B, startRound basics.Round) {
}
}
+func triggerTrackerFlush(t *testing.T, l *Ledger, genesisInitState ledgercore.InitState) {
+ l.trackers.mu.RLock()
+ initialDbRound := l.trackers.dbRound
+ currentDbRound := initialDbRound
+ l.trackers.lastFlushTime = time.Time{}
+ l.trackers.mu.RUnlock()
+
+ addEmptyValidatedBlock(t, l, genesisInitState.Accounts)
+
+ const timeout = 2 * time.Second
+ started := time.Now()
+
+ // We can't truly wait for scheduleCommit to take place, which means without waiting using sleeps
+ // we might beat scheduleCommit's addition to accountsWriting, making our wait on it continue immediately.
+ // The solution is to wait for the advancement of l.trackers.dbRound, which is a side effect of postCommit's success.
+ for currentDbRound == initialDbRound {
+ time.Sleep(50 * time.Microsecond)
+ require.True(t, time.Now().Sub(started) < timeout)
+ l.trackers.mu.RLock()
+ currentDbRound = l.trackers.dbRound
+ l.trackers.mu.RUnlock()
+ }
+ l.trackers.waitAccountsWriting()
+}
+
func TestLedgerReload(t *testing.T) {
partitiontest.PartitionTest(t)
@@ -1513,6 +1539,36 @@ func TestLedgerReload(t *testing.T) {
}
}
+func TestWaitLedgerReload(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ a := require.New(t)
+
+ dbName := fmt.Sprintf("%s.%d", t.Name(), crypto.RandUint64())
+ genesisInitState, _ := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
+ const inMem = true
+ cfg := config.GetDefaultLocal()
+ cfg.MaxAcctLookback = 0
+ log := logging.TestingLog(t)
+ log.SetLevel(logging.Info)
+ l, err := OpenLedger(log, dbName, inMem, genesisInitState, cfg)
+ require.NoError(t, err)
+ defer l.Close()
+
+ waitRound := l.Latest() + 1
+ waitChannel := l.Wait(waitRound)
+
+ err = l.reloadLedger()
+ a.NoError(err)
+ triggerTrackerFlush(t, l, genesisInitState)
+
+ select {
+ case <-waitChannel:
+ return
+ default:
+ a.Failf("", "Wait channel did not receive an expected signal for round %d", waitRound)
+ }
+}
+
// TestGetLastCatchpointLabel tests ledger.GetLastCatchpointLabel is returning the correct value.
func TestGetLastCatchpointLabel(t *testing.T) {
partitiontest.PartitionTest(t)
diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go
index bafd9f32a..69e2c543e 100644
--- a/ledger/ledgercore/accountdata.go
+++ b/ledger/ledgercore/accountdata.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/accountdata_test.go b/ledger/ledgercore/accountdata_test.go
index a22a6b5b0..76eb82d0d 100644
--- a/ledger/ledgercore/accountdata_test.go
+++ b/ledger/ledgercore/accountdata_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/accountresource.go b/ledger/ledgercore/accountresource.go
index 5ccb1e53d..4d22ec0dc 100644
--- a/ledger/ledgercore/accountresource.go
+++ b/ledger/ledgercore/accountresource.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/catchpointlabel.go b/ledger/ledgercore/catchpointlabel.go
index 2b2e085fa..6f0b90f9d 100644
--- a/ledger/ledgercore/catchpointlabel.go
+++ b/ledger/ledgercore/catchpointlabel.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/catchpointlabel_test.go b/ledger/ledgercore/catchpointlabel_test.go
index 5e7a22026..1bfbe3115 100644
--- a/ledger/ledgercore/catchpointlabel_test.go
+++ b/ledger/ledgercore/catchpointlabel_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/error.go b/ledger/ledgercore/error.go
index 28311973c..b1dff71a4 100644
--- a/ledger/ledgercore/error.go
+++ b/ledger/ledgercore/error.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/misc.go b/ledger/ledgercore/misc.go
index 5bc39cd1c..86fc2c3ff 100644
--- a/ledger/ledgercore/misc.go
+++ b/ledger/ledgercore/misc.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -28,3 +28,8 @@ type InitState struct {
Accounts map[basics.Address]basics.AccountData
GenesisHash crypto.Digest
}
+
+// BlockListener represents an object that needs to get notified on new blocks.
+type BlockListener interface {
+ OnNewBlock(block bookkeeping.Block, delta StateDelta)
+}
diff --git a/ledger/ledgercore/onlineacct.go b/ledger/ledgercore/onlineacct.go
index 765067bb2..690ebcfe5 100644
--- a/ledger/ledgercore/onlineacct.go
+++ b/ledger/ledgercore/onlineacct.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/statedelta.go b/ledger/ledgercore/statedelta.go
index 688c8d093..7672f0a86 100644
--- a/ledger/ledgercore/statedelta.go
+++ b/ledger/ledgercore/statedelta.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/statedelta_test.go b/ledger/ledgercore/statedelta_test.go
index aeae35b36..dd0f3e201 100644
--- a/ledger/ledgercore/statedelta_test.go
+++ b/ledger/ledgercore/statedelta_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/totals.go b/ledger/ledgercore/totals.go
index 291db77a2..4e75ddbf4 100644
--- a/ledger/ledgercore/totals.go
+++ b/ledger/ledgercore/totals.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/totals_test.go b/ledger/ledgercore/totals_test.go
index 12d3b9a2a..66f4e09ac 100644
--- a/ledger/ledgercore/totals_test.go
+++ b/ledger/ledgercore/totals_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go
index 9b4aa30f1..84d09e2a9 100644
--- a/ledger/ledgercore/validatedBlock.go
+++ b/ledger/ledgercore/validatedBlock.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/ledgercore/votersForRound.go b/ledger/ledgercore/votersForRound.go
index 1212778b3..c1afd9eb5 100644
--- a/ledger/ledgercore/votersForRound.go
+++ b/ledger/ledgercore/votersForRound.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/lruaccts.go b/ledger/lruaccts.go
index 13e639a95..22928012a 100644
--- a/ledger/lruaccts.go
+++ b/ledger/lruaccts.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/lruaccts_test.go b/ledger/lruaccts_test.go
index 30f1f082c..625d40f3e 100644
--- a/ledger/lruaccts_test.go
+++ b/ledger/lruaccts_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/lrukv.go b/ledger/lrukv.go
index 30530fe6b..77986eb1b 100644
--- a/ledger/lrukv.go
+++ b/ledger/lrukv.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/lrukv_test.go b/ledger/lrukv_test.go
index 01ed00206..18d0a4707 100644
--- a/ledger/lrukv_test.go
+++ b/ledger/lrukv_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/lruonlineaccts.go b/ledger/lruonlineaccts.go
index bc8ccedfd..35c8224d4 100644
--- a/ledger/lruonlineaccts.go
+++ b/ledger/lruonlineaccts.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/lruonlineaccts_test.go b/ledger/lruonlineaccts_test.go
index 2b9994480..0e0b31438 100644
--- a/ledger/lruonlineaccts_test.go
+++ b/ledger/lruonlineaccts_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/lruresources.go b/ledger/lruresources.go
index b2f9a63b0..222c1d6c0 100644
--- a/ledger/lruresources.go
+++ b/ledger/lruresources.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/lruresources_test.go b/ledger/lruresources_test.go
index 695d1f026..6c9730903 100644
--- a/ledger/lruresources_test.go
+++ b/ledger/lruresources_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/metrics.go b/ledger/metrics.go
index 66a346678..4700dfcd5 100644
--- a/ledger/metrics.go
+++ b/ledger/metrics.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -56,10 +56,10 @@ func (mt *metricsTracker) close() {
func (mt *metricsTracker) newBlock(blk bookkeeping.Block, delta ledgercore.StateDelta) {
rnd := blk.Round()
- mt.ledgerRound.Set(float64(rnd))
- mt.ledgerTransactionsTotal.Add(float64(len(blk.Payset)), map[string]string{})
+ mt.ledgerRound.Set(uint64(rnd))
+ mt.ledgerTransactionsTotal.AddUint64(uint64(len(blk.Payset)), nil)
// TODO rewards: need to provide meaningful metric here.
- mt.ledgerRewardClaimsTotal.Add(float64(1), map[string]string{})
+ mt.ledgerRewardClaimsTotal.Inc(nil)
}
func (mt *metricsTracker) committedUpTo(committedRnd basics.Round) (retRound, lookback basics.Round) {
diff --git a/ledger/metrics_test.go b/ledger/metrics_test.go
index ca24e0f0f..bca0b6bcc 100644
--- a/ledger/metrics_test.go
+++ b/ledger/metrics_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/msgp_gen.go b/ledger/msgp_gen.go
index ddd01a87f..39c489e28 100644
--- a/ledger/msgp_gen.go
+++ b/ledger/msgp_gen.go
@@ -3,9 +3,9 @@ package ledger
// Code generated by github.com/algorand/msgp DO NOT EDIT.
import (
- "sort"
-
"github.com/algorand/msgp/msgp"
+
+ "github.com/algorand/go-algorand/ledger/encoded"
)
// The following msgp objects are implemented in this file:
@@ -41,30 +41,6 @@ import (
// |-----> (*) Msgsize
// |-----> (*) MsgIsZero
//
-// encodedBalanceRecordV5
-// |-----> (*) MarshalMsg
-// |-----> (*) CanMarshalMsg
-// |-----> (*) UnmarshalMsg
-// |-----> (*) CanUnmarshalMsg
-// |-----> (*) Msgsize
-// |-----> (*) MsgIsZero
-//
-// encodedBalanceRecordV6
-// |-----> (*) MarshalMsg
-// |-----> (*) CanMarshalMsg
-// |-----> (*) UnmarshalMsg
-// |-----> (*) CanUnmarshalMsg
-// |-----> (*) Msgsize
-// |-----> (*) MsgIsZero
-//
-// encodedKVRecordV6
-// |-----> (*) MarshalMsg
-// |-----> (*) CanMarshalMsg
-// |-----> (*) UnmarshalMsg
-// |-----> (*) CanUnmarshalMsg
-// |-----> (*) Msgsize
-// |-----> (*) MsgIsZero
-//
// MarshalMsg implements msgp.Marshaler
func (z CatchpointCatchupState) MarshalMsg(b []byte) (o []byte) {
@@ -424,29 +400,7 @@ func (z *catchpointFileBalancesChunkV5) MarshalMsg(b []byte) (o []byte) {
o = msgp.AppendArrayHeader(o, uint32(len((*z).Balances)))
}
for zb0001 := range (*z).Balances {
- // omitempty: check for empty values
- zb0003Len := uint32(2)
- var zb0003Mask uint8 /* 3 bits */
- if (*z).Balances[zb0001].AccountData.MsgIsZero() {
- zb0003Len--
- zb0003Mask |= 0x2
- }
- if (*z).Balances[zb0001].Address.MsgIsZero() {
- zb0003Len--
- zb0003Mask |= 0x4
- }
- // variable map header, size zb0003Len
- o = append(o, 0x80|uint8(zb0003Len))
- if (zb0003Mask & 0x2) == 0 { // if not empty
- // string "ad"
- o = append(o, 0xa2, 0x61, 0x64)
- o = (*z).Balances[zb0001].AccountData.MarshalMsg(o)
- }
- if (zb0003Mask & 0x4) == 0 { // if not empty
- // string "pk"
- o = append(o, 0xa2, 0x70, 0x6b)
- o = (*z).Balances[zb0001].Address.MarshalMsg(o)
- }
+ o = (*z).Balances[zb0001].MarshalMsg(o)
}
}
}
@@ -490,77 +444,13 @@ func (z *catchpointFileBalancesChunkV5) UnmarshalMsg(bts []byte) (o []byte, err
} else if (*z).Balances != nil && cap((*z).Balances) >= zb0004 {
(*z).Balances = ((*z).Balances)[:zb0004]
} else {
- (*z).Balances = make([]encodedBalanceRecordV5, zb0004)
+ (*z).Balances = make([]encoded.BalanceRecordV5, zb0004)
}
for zb0001 := range (*z).Balances {
- var zb0006 int
- var zb0007 bool
- zb0006, zb0007, bts, err = msgp.ReadMapHeaderBytes(bts)
- if _, ok := err.(msgp.TypeError); ok {
- zb0006, zb0007, bts, err = msgp.ReadArrayHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001)
- return
- }
- if zb0006 > 0 {
- zb0006--
- bts, err = (*z).Balances[zb0001].Address.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001, "struct-from-array", "Address")
- return
- }
- }
- if zb0006 > 0 {
- zb0006--
- bts, err = (*z).Balances[zb0001].AccountData.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001, "struct-from-array", "AccountData")
- return
- }
- }
- if zb0006 > 0 {
- err = msgp.ErrTooManyArrayFields(zb0006)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001, "struct-from-array")
- return
- }
- }
- } else {
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001)
- return
- }
- if zb0007 {
- (*z).Balances[zb0001] = encodedBalanceRecordV5{}
- }
- for zb0006 > 0 {
- zb0006--
- field, bts, err = msgp.ReadMapKeyZC(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001)
- return
- }
- switch string(field) {
- case "pk":
- bts, err = (*z).Balances[zb0001].Address.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001, "Address")
- return
- }
- case "ad":
- bts, err = (*z).Balances[zb0001].AccountData.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001, "AccountData")
- return
- }
- default:
- err = msgp.ErrNoField(string(field))
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001)
- return
- }
- }
- }
+ bts, err = (*z).Balances[zb0001].UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Balances", zb0001)
+ return
}
}
}
@@ -588,94 +478,30 @@ func (z *catchpointFileBalancesChunkV5) UnmarshalMsg(bts []byte) (o []byte, err
}
switch string(field) {
case "bl":
- var zb0008 int
- var zb0009 bool
- zb0008, zb0009, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ var zb0006 int
+ var zb0007 bool
+ zb0006, zb0007, bts, err = msgp.ReadArrayHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Balances")
return
}
- if zb0008 > BalancesPerCatchpointFileChunk {
- err = msgp.ErrOverflow(uint64(zb0008), uint64(BalancesPerCatchpointFileChunk))
+ if zb0006 > BalancesPerCatchpointFileChunk {
+ err = msgp.ErrOverflow(uint64(zb0006), uint64(BalancesPerCatchpointFileChunk))
err = msgp.WrapError(err, "Balances")
return
}
- if zb0009 {
+ if zb0007 {
(*z).Balances = nil
- } else if (*z).Balances != nil && cap((*z).Balances) >= zb0008 {
- (*z).Balances = ((*z).Balances)[:zb0008]
+ } else if (*z).Balances != nil && cap((*z).Balances) >= zb0006 {
+ (*z).Balances = ((*z).Balances)[:zb0006]
} else {
- (*z).Balances = make([]encodedBalanceRecordV5, zb0008)
+ (*z).Balances = make([]encoded.BalanceRecordV5, zb0006)
}
for zb0001 := range (*z).Balances {
- var zb0010 int
- var zb0011 bool
- zb0010, zb0011, bts, err = msgp.ReadMapHeaderBytes(bts)
- if _, ok := err.(msgp.TypeError); ok {
- zb0010, zb0011, bts, err = msgp.ReadArrayHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001)
- return
- }
- if zb0010 > 0 {
- zb0010--
- bts, err = (*z).Balances[zb0001].Address.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001, "struct-from-array", "Address")
- return
- }
- }
- if zb0010 > 0 {
- zb0010--
- bts, err = (*z).Balances[zb0001].AccountData.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001, "struct-from-array", "AccountData")
- return
- }
- }
- if zb0010 > 0 {
- err = msgp.ErrTooManyArrayFields(zb0010)
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001, "struct-from-array")
- return
- }
- }
- } else {
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001)
- return
- }
- if zb0011 {
- (*z).Balances[zb0001] = encodedBalanceRecordV5{}
- }
- for zb0010 > 0 {
- zb0010--
- field, bts, err = msgp.ReadMapKeyZC(bts)
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001)
- return
- }
- switch string(field) {
- case "pk":
- bts, err = (*z).Balances[zb0001].Address.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001, "Address")
- return
- }
- case "ad":
- bts, err = (*z).Balances[zb0001].AccountData.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001, "AccountData")
- return
- }
- default:
- err = msgp.ErrNoField(string(field))
- if err != nil {
- err = msgp.WrapError(err, "Balances", zb0001)
- return
- }
- }
- }
+ bts, err = (*z).Balances[zb0001].UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Balances", zb0001)
+ return
}
}
default:
@@ -700,7 +526,7 @@ func (_ *catchpointFileBalancesChunkV5) CanUnmarshalMsg(z interface{}) bool {
func (z *catchpointFileBalancesChunkV5) Msgsize() (s int) {
s = 1 + 3 + msgp.ArrayHeaderSize
for zb0001 := range (*z).Balances {
- s += 1 + 3 + (*z).Balances[zb0001].Address.Msgsize() + 3 + (*z).Balances[zb0001].AccountData.Msgsize()
+ s += (*z).Balances[zb0001].Msgsize()
}
return
}
@@ -748,29 +574,7 @@ func (z *catchpointFileChunkV6) MarshalMsg(b []byte) (o []byte) {
o = msgp.AppendArrayHeader(o, uint32(len((*z).KVs)))
}
for zb0002 := range (*z).KVs {
- // omitempty: check for empty values
- zb0004Len := uint32(2)
- var zb0004Mask uint8 /* 3 bits */
- if len((*z).KVs[zb0002].Key) == 0 {
- zb0004Len--
- zb0004Mask |= 0x2
- }
- if len((*z).KVs[zb0002].Value) == 0 {
- zb0004Len--
- zb0004Mask |= 0x4
- }
- // variable map header, size zb0004Len
- o = append(o, 0x80|uint8(zb0004Len))
- if (zb0004Mask & 0x2) == 0 { // if not empty
- // string "k"
- o = append(o, 0xa1, 0x6b)
- o = msgp.AppendBytes(o, (*z).KVs[zb0002].Key)
- }
- if (zb0004Mask & 0x4) == 0 { // if not empty
- // string "v"
- o = append(o, 0xa1, 0x76)
- o = msgp.AppendBytes(o, (*z).KVs[zb0002].Value)
- }
+ o = (*z).KVs[zb0002].MarshalMsg(o)
}
}
}
@@ -814,7 +618,7 @@ func (z *catchpointFileChunkV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
} else if (*z).Balances != nil && cap((*z).Balances) >= zb0005 {
(*z).Balances = ((*z).Balances)[:zb0005]
} else {
- (*z).Balances = make([]encodedBalanceRecordV6, zb0005)
+ (*z).Balances = make([]encoded.BalanceRecordV6, zb0005)
}
for zb0001 := range (*z).Balances {
bts, err = (*z).Balances[zb0001].UnmarshalMsg(bts)
@@ -843,117 +647,13 @@ func (z *catchpointFileChunkV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
} else if (*z).KVs != nil && cap((*z).KVs) >= zb0007 {
(*z).KVs = ((*z).KVs)[:zb0007]
} else {
- (*z).KVs = make([]encodedKVRecordV6, zb0007)
+ (*z).KVs = make([]encoded.KVRecordV6, zb0007)
}
for zb0002 := range (*z).KVs {
- var zb0009 int
- var zb0010 bool
- zb0009, zb0010, bts, err = msgp.ReadMapHeaderBytes(bts)
- if _, ok := err.(msgp.TypeError); ok {
- zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002)
- return
- }
- if zb0009 > 0 {
- zb0009--
- var zb0011 int
- zb0011, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "struct-from-array", "Key")
- return
- }
- if zb0011 > encodedKVRecordV6MaxKeyLength {
- err = msgp.ErrOverflow(uint64(zb0011), uint64(encodedKVRecordV6MaxKeyLength))
- return
- }
- (*z).KVs[zb0002].Key, bts, err = msgp.ReadBytesBytes(bts, (*z).KVs[zb0002].Key)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "struct-from-array", "Key")
- return
- }
- }
- if zb0009 > 0 {
- zb0009--
- var zb0012 int
- zb0012, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "struct-from-array", "Value")
- return
- }
- if zb0012 > encodedKVRecordV6MaxValueLength {
- err = msgp.ErrOverflow(uint64(zb0012), uint64(encodedKVRecordV6MaxValueLength))
- return
- }
- (*z).KVs[zb0002].Value, bts, err = msgp.ReadBytesBytes(bts, (*z).KVs[zb0002].Value)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "struct-from-array", "Value")
- return
- }
- }
- if zb0009 > 0 {
- err = msgp.ErrTooManyArrayFields(zb0009)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "struct-from-array")
- return
- }
- }
- } else {
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002)
- return
- }
- if zb0010 {
- (*z).KVs[zb0002] = encodedKVRecordV6{}
- }
- for zb0009 > 0 {
- zb0009--
- field, bts, err = msgp.ReadMapKeyZC(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002)
- return
- }
- switch string(field) {
- case "k":
- var zb0013 int
- zb0013, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "Key")
- return
- }
- if zb0013 > encodedKVRecordV6MaxKeyLength {
- err = msgp.ErrOverflow(uint64(zb0013), uint64(encodedKVRecordV6MaxKeyLength))
- return
- }
- (*z).KVs[zb0002].Key, bts, err = msgp.ReadBytesBytes(bts, (*z).KVs[zb0002].Key)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "Key")
- return
- }
- case "v":
- var zb0014 int
- zb0014, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "Value")
- return
- }
- if zb0014 > encodedKVRecordV6MaxValueLength {
- err = msgp.ErrOverflow(uint64(zb0014), uint64(encodedKVRecordV6MaxValueLength))
- return
- }
- (*z).KVs[zb0002].Value, bts, err = msgp.ReadBytesBytes(bts, (*z).KVs[zb0002].Value)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002, "Value")
- return
- }
- default:
- err = msgp.ErrNoField(string(field))
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002)
- return
- }
- }
- }
+ bts, err = (*z).KVs[zb0002].UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "KVs", zb0002)
+ return
}
}
}
@@ -981,24 +681,24 @@ func (z *catchpointFileChunkV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
}
switch string(field) {
case "bl":
- var zb0015 int
- var zb0016 bool
- zb0015, zb0016, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ var zb0009 int
+ var zb0010 bool
+ zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err, "Balances")
return
}
- if zb0015 > BalancesPerCatchpointFileChunk {
- err = msgp.ErrOverflow(uint64(zb0015), uint64(BalancesPerCatchpointFileChunk))
+ if zb0009 > BalancesPerCatchpointFileChunk {
+ err = msgp.ErrOverflow(uint64(zb0009), uint64(BalancesPerCatchpointFileChunk))
err = msgp.WrapError(err, "Balances")
return
}
- if zb0016 {
+ if zb0010 {
(*z).Balances = nil
- } else if (*z).Balances != nil && cap((*z).Balances) >= zb0015 {
- (*z).Balances = ((*z).Balances)[:zb0015]
+ } else if (*z).Balances != nil && cap((*z).Balances) >= zb0009 {
+ (*z).Balances = ((*z).Balances)[:zb0009]
} else {
- (*z).Balances = make([]encodedBalanceRecordV6, zb0015)
+ (*z).Balances = make([]encoded.BalanceRecordV6, zb0009)
}
for zb0001 := range (*z).Balances {
bts, err = (*z).Balances[zb0001].UnmarshalMsg(bts)
@@ -1008,134 +708,30 @@ func (z *catchpointFileChunkV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
}
}
case "kv":
- var zb0017 int
- var zb0018 bool
- zb0017, zb0018, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ var zb0011 int
+ var zb0012 bool
+ zb0011, zb0012, bts, err = msgp.ReadArrayHeaderBytes(bts)
if err != nil {
err = msgp.WrapError(err, "KVs")
return
}
- if zb0017 > BalancesPerCatchpointFileChunk {
- err = msgp.ErrOverflow(uint64(zb0017), uint64(BalancesPerCatchpointFileChunk))
+ if zb0011 > BalancesPerCatchpointFileChunk {
+ err = msgp.ErrOverflow(uint64(zb0011), uint64(BalancesPerCatchpointFileChunk))
err = msgp.WrapError(err, "KVs")
return
}
- if zb0018 {
+ if zb0012 {
(*z).KVs = nil
- } else if (*z).KVs != nil && cap((*z).KVs) >= zb0017 {
- (*z).KVs = ((*z).KVs)[:zb0017]
+ } else if (*z).KVs != nil && cap((*z).KVs) >= zb0011 {
+ (*z).KVs = ((*z).KVs)[:zb0011]
} else {
- (*z).KVs = make([]encodedKVRecordV6, zb0017)
+ (*z).KVs = make([]encoded.KVRecordV6, zb0011)
}
for zb0002 := range (*z).KVs {
- var zb0019 int
- var zb0020 bool
- zb0019, zb0020, bts, err = msgp.ReadMapHeaderBytes(bts)
- if _, ok := err.(msgp.TypeError); ok {
- zb0019, zb0020, bts, err = msgp.ReadArrayHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002)
- return
- }
- if zb0019 > 0 {
- zb0019--
- var zb0021 int
- zb0021, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "struct-from-array", "Key")
- return
- }
- if zb0021 > encodedKVRecordV6MaxKeyLength {
- err = msgp.ErrOverflow(uint64(zb0021), uint64(encodedKVRecordV6MaxKeyLength))
- return
- }
- (*z).KVs[zb0002].Key, bts, err = msgp.ReadBytesBytes(bts, (*z).KVs[zb0002].Key)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "struct-from-array", "Key")
- return
- }
- }
- if zb0019 > 0 {
- zb0019--
- var zb0022 int
- zb0022, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "struct-from-array", "Value")
- return
- }
- if zb0022 > encodedKVRecordV6MaxValueLength {
- err = msgp.ErrOverflow(uint64(zb0022), uint64(encodedKVRecordV6MaxValueLength))
- return
- }
- (*z).KVs[zb0002].Value, bts, err = msgp.ReadBytesBytes(bts, (*z).KVs[zb0002].Value)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "struct-from-array", "Value")
- return
- }
- }
- if zb0019 > 0 {
- err = msgp.ErrTooManyArrayFields(zb0019)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "struct-from-array")
- return
- }
- }
- } else {
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002)
- return
- }
- if zb0020 {
- (*z).KVs[zb0002] = encodedKVRecordV6{}
- }
- for zb0019 > 0 {
- zb0019--
- field, bts, err = msgp.ReadMapKeyZC(bts)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002)
- return
- }
- switch string(field) {
- case "k":
- var zb0023 int
- zb0023, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "Key")
- return
- }
- if zb0023 > encodedKVRecordV6MaxKeyLength {
- err = msgp.ErrOverflow(uint64(zb0023), uint64(encodedKVRecordV6MaxKeyLength))
- return
- }
- (*z).KVs[zb0002].Key, bts, err = msgp.ReadBytesBytes(bts, (*z).KVs[zb0002].Key)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "Key")
- return
- }
- case "v":
- var zb0024 int
- zb0024, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "Value")
- return
- }
- if zb0024 > encodedKVRecordV6MaxValueLength {
- err = msgp.ErrOverflow(uint64(zb0024), uint64(encodedKVRecordV6MaxValueLength))
- return
- }
- (*z).KVs[zb0002].Value, bts, err = msgp.ReadBytesBytes(bts, (*z).KVs[zb0002].Value)
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002, "Value")
- return
- }
- default:
- err = msgp.ErrNoField(string(field))
- if err != nil {
- err = msgp.WrapError(err, "KVs", zb0002)
- return
- }
- }
- }
+ bts, err = (*z).KVs[zb0002].UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "KVs", zb0002)
+ return
}
}
default:
@@ -1164,7 +760,7 @@ func (z *catchpointFileChunkV6) Msgsize() (s int) {
}
s += 3 + msgp.ArrayHeaderSize
for zb0002 := range (*z).KVs {
- s += 1 + 2 + msgp.BytesPrefixSize + len((*z).KVs[zb0002].Key) + 2 + msgp.BytesPrefixSize + len((*z).KVs[zb0002].Value)
+ s += (*z).KVs[zb0002].Msgsize()
}
return
}
@@ -1173,555 +769,3 @@ func (z *catchpointFileChunkV6) Msgsize() (s int) {
func (z *catchpointFileChunkV6) MsgIsZero() bool {
return (len((*z).Balances) == 0) && (len((*z).KVs) == 0)
}
-
-// MarshalMsg implements msgp.Marshaler
-func (z *encodedBalanceRecordV5) MarshalMsg(b []byte) (o []byte) {
- o = msgp.Require(b, z.Msgsize())
- // omitempty: check for empty values
- zb0001Len := uint32(2)
- var zb0001Mask uint8 /* 3 bits */
- if (*z).AccountData.MsgIsZero() {
- zb0001Len--
- zb0001Mask |= 0x2
- }
- if (*z).Address.MsgIsZero() {
- zb0001Len--
- zb0001Mask |= 0x4
- }
- // variable map header, size zb0001Len
- o = append(o, 0x80|uint8(zb0001Len))
- if zb0001Len != 0 {
- if (zb0001Mask & 0x2) == 0 { // if not empty
- // string "ad"
- o = append(o, 0xa2, 0x61, 0x64)
- o = (*z).AccountData.MarshalMsg(o)
- }
- if (zb0001Mask & 0x4) == 0 { // if not empty
- // string "pk"
- o = append(o, 0xa2, 0x70, 0x6b)
- o = (*z).Address.MarshalMsg(o)
- }
- }
- return
-}
-
-func (_ *encodedBalanceRecordV5) CanMarshalMsg(z interface{}) bool {
- _, ok := (z).(*encodedBalanceRecordV5)
- return ok
-}
-
-// UnmarshalMsg implements msgp.Unmarshaler
-func (z *encodedBalanceRecordV5) UnmarshalMsg(bts []byte) (o []byte, err error) {
- var field []byte
- _ = field
- var zb0001 int
- var zb0002 bool
- zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
- if _, ok := err.(msgp.TypeError); ok {
- zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- if zb0001 > 0 {
- zb0001--
- bts, err = (*z).Address.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Address")
- return
- }
- }
- if zb0001 > 0 {
- zb0001--
- bts, err = (*z).AccountData.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "AccountData")
- return
- }
- }
- if zb0001 > 0 {
- err = msgp.ErrTooManyArrayFields(zb0001)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array")
- return
- }
- }
- } else {
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- if zb0002 {
- (*z) = encodedBalanceRecordV5{}
- }
- for zb0001 > 0 {
- zb0001--
- field, bts, err = msgp.ReadMapKeyZC(bts)
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- switch string(field) {
- case "pk":
- bts, err = (*z).Address.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "Address")
- return
- }
- case "ad":
- bts, err = (*z).AccountData.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "AccountData")
- return
- }
- default:
- err = msgp.ErrNoField(string(field))
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- }
- }
- }
- o = bts
- return
-}
-
-func (_ *encodedBalanceRecordV5) CanUnmarshalMsg(z interface{}) bool {
- _, ok := (z).(*encodedBalanceRecordV5)
- return ok
-}
-
-// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
-func (z *encodedBalanceRecordV5) Msgsize() (s int) {
- s = 1 + 3 + (*z).Address.Msgsize() + 3 + (*z).AccountData.Msgsize()
- return
-}
-
-// MsgIsZero returns whether this is a zero value
-func (z *encodedBalanceRecordV5) MsgIsZero() bool {
- return ((*z).Address.MsgIsZero()) && ((*z).AccountData.MsgIsZero())
-}
-
-// MarshalMsg implements msgp.Marshaler
-func (z *encodedBalanceRecordV6) MarshalMsg(b []byte) (o []byte) {
- o = msgp.Require(b, z.Msgsize())
- // omitempty: check for empty values
- zb0003Len := uint32(4)
- var zb0003Mask uint8 /* 5 bits */
- if (*z).Address.MsgIsZero() {
- zb0003Len--
- zb0003Mask |= 0x2
- }
- if (*z).AccountData.MsgIsZero() {
- zb0003Len--
- zb0003Mask |= 0x4
- }
- if len((*z).Resources) == 0 {
- zb0003Len--
- zb0003Mask |= 0x8
- }
- if (*z).ExpectingMoreEntries == false {
- zb0003Len--
- zb0003Mask |= 0x10
- }
- // variable map header, size zb0003Len
- o = append(o, 0x80|uint8(zb0003Len))
- if zb0003Len != 0 {
- if (zb0003Mask & 0x2) == 0 { // if not empty
- // string "a"
- o = append(o, 0xa1, 0x61)
- o = (*z).Address.MarshalMsg(o)
- }
- if (zb0003Mask & 0x4) == 0 { // if not empty
- // string "b"
- o = append(o, 0xa1, 0x62)
- o = (*z).AccountData.MarshalMsg(o)
- }
- if (zb0003Mask & 0x8) == 0 { // if not empty
- // string "c"
- o = append(o, 0xa1, 0x63)
- if (*z).Resources == nil {
- o = msgp.AppendNil(o)
- } else {
- o = msgp.AppendMapHeader(o, uint32(len((*z).Resources)))
- }
- zb0001_keys := make([]uint64, 0, len((*z).Resources))
- for zb0001 := range (*z).Resources {
- zb0001_keys = append(zb0001_keys, zb0001)
- }
- sort.Sort(SortUint64(zb0001_keys))
- for _, zb0001 := range zb0001_keys {
- zb0002 := (*z).Resources[zb0001]
- _ = zb0002
- o = msgp.AppendUint64(o, zb0001)
- o = zb0002.MarshalMsg(o)
- }
- }
- if (zb0003Mask & 0x10) == 0 { // if not empty
- // string "e"
- o = append(o, 0xa1, 0x65)
- o = msgp.AppendBool(o, (*z).ExpectingMoreEntries)
- }
- }
- return
-}
-
-func (_ *encodedBalanceRecordV6) CanMarshalMsg(z interface{}) bool {
- _, ok := (z).(*encodedBalanceRecordV6)
- return ok
-}
-
-// UnmarshalMsg implements msgp.Unmarshaler
-func (z *encodedBalanceRecordV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
- var field []byte
- _ = field
- var zb0003 int
- var zb0004 bool
- zb0003, zb0004, bts, err = msgp.ReadMapHeaderBytes(bts)
- if _, ok := err.(msgp.TypeError); ok {
- zb0003, zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- if zb0003 > 0 {
- zb0003--
- bts, err = (*z).Address.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Address")
- return
- }
- }
- if zb0003 > 0 {
- zb0003--
- bts, err = (*z).AccountData.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "AccountData")
- return
- }
- }
- if zb0003 > 0 {
- zb0003--
- var zb0005 int
- var zb0006 bool
- zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Resources")
- return
- }
- if zb0005 > resourcesPerCatchpointFileChunkBackwardCompatible {
- err = msgp.ErrOverflow(uint64(zb0005), uint64(resourcesPerCatchpointFileChunkBackwardCompatible))
- err = msgp.WrapError(err, "struct-from-array", "Resources")
- return
- }
- if zb0006 {
- (*z).Resources = nil
- } else if (*z).Resources == nil {
- (*z).Resources = make(map[uint64]msgp.Raw, zb0005)
- }
- for zb0005 > 0 {
- var zb0001 uint64
- var zb0002 msgp.Raw
- zb0005--
- zb0001, bts, err = msgp.ReadUint64Bytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Resources")
- return
- }
- bts, err = zb0002.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Resources", zb0001)
- return
- }
- (*z).Resources[zb0001] = zb0002
- }
- }
- if zb0003 > 0 {
- zb0003--
- (*z).ExpectingMoreEntries, bts, err = msgp.ReadBoolBytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "ExpectingMoreEntries")
- return
- }
- }
- if zb0003 > 0 {
- err = msgp.ErrTooManyArrayFields(zb0003)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array")
- return
- }
- }
- } else {
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- if zb0004 {
- (*z) = encodedBalanceRecordV6{}
- }
- for zb0003 > 0 {
- zb0003--
- field, bts, err = msgp.ReadMapKeyZC(bts)
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- switch string(field) {
- case "a":
- bts, err = (*z).Address.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "Address")
- return
- }
- case "b":
- bts, err = (*z).AccountData.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "AccountData")
- return
- }
- case "c":
- var zb0007 int
- var zb0008 bool
- zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "Resources")
- return
- }
- if zb0007 > resourcesPerCatchpointFileChunkBackwardCompatible {
- err = msgp.ErrOverflow(uint64(zb0007), uint64(resourcesPerCatchpointFileChunkBackwardCompatible))
- err = msgp.WrapError(err, "Resources")
- return
- }
- if zb0008 {
- (*z).Resources = nil
- } else if (*z).Resources == nil {
- (*z).Resources = make(map[uint64]msgp.Raw, zb0007)
- }
- for zb0007 > 0 {
- var zb0001 uint64
- var zb0002 msgp.Raw
- zb0007--
- zb0001, bts, err = msgp.ReadUint64Bytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "Resources")
- return
- }
- bts, err = zb0002.UnmarshalMsg(bts)
- if err != nil {
- err = msgp.WrapError(err, "Resources", zb0001)
- return
- }
- (*z).Resources[zb0001] = zb0002
- }
- case "e":
- (*z).ExpectingMoreEntries, bts, err = msgp.ReadBoolBytes(bts)
- if err != nil {
- err = msgp.WrapError(err, "ExpectingMoreEntries")
- return
- }
- default:
- err = msgp.ErrNoField(string(field))
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- }
- }
- }
- o = bts
- return
-}
-
-func (_ *encodedBalanceRecordV6) CanUnmarshalMsg(z interface{}) bool {
- _, ok := (z).(*encodedBalanceRecordV6)
- return ok
-}
-
-// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
-func (z *encodedBalanceRecordV6) Msgsize() (s int) {
- s = 1 + 2 + (*z).Address.Msgsize() + 2 + (*z).AccountData.Msgsize() + 2 + msgp.MapHeaderSize
- if (*z).Resources != nil {
- for zb0001, zb0002 := range (*z).Resources {
- _ = zb0001
- _ = zb0002
- s += 0 + msgp.Uint64Size + zb0002.Msgsize()
- }
- }
- s += 2 + msgp.BoolSize
- return
-}
-
-// MsgIsZero returns whether this is a zero value
-func (z *encodedBalanceRecordV6) MsgIsZero() bool {
- return ((*z).Address.MsgIsZero()) && ((*z).AccountData.MsgIsZero()) && (len((*z).Resources) == 0) && ((*z).ExpectingMoreEntries == false)
-}
-
-// MarshalMsg implements msgp.Marshaler
-func (z *encodedKVRecordV6) MarshalMsg(b []byte) (o []byte) {
- o = msgp.Require(b, z.Msgsize())
- // omitempty: check for empty values
- zb0001Len := uint32(2)
- var zb0001Mask uint8 /* 3 bits */
- if len((*z).Key) == 0 {
- zb0001Len--
- zb0001Mask |= 0x2
- }
- if len((*z).Value) == 0 {
- zb0001Len--
- zb0001Mask |= 0x4
- }
- // variable map header, size zb0001Len
- o = append(o, 0x80|uint8(zb0001Len))
- if zb0001Len != 0 {
- if (zb0001Mask & 0x2) == 0 { // if not empty
- // string "k"
- o = append(o, 0xa1, 0x6b)
- o = msgp.AppendBytes(o, (*z).Key)
- }
- if (zb0001Mask & 0x4) == 0 { // if not empty
- // string "v"
- o = append(o, 0xa1, 0x76)
- o = msgp.AppendBytes(o, (*z).Value)
- }
- }
- return
-}
-
-func (_ *encodedKVRecordV6) CanMarshalMsg(z interface{}) bool {
- _, ok := (z).(*encodedKVRecordV6)
- return ok
-}
-
-// UnmarshalMsg implements msgp.Unmarshaler
-func (z *encodedKVRecordV6) UnmarshalMsg(bts []byte) (o []byte, err error) {
- var field []byte
- _ = field
- var zb0001 int
- var zb0002 bool
- zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
- if _, ok := err.(msgp.TypeError); ok {
- zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- if zb0001 > 0 {
- zb0001--
- var zb0003 int
- zb0003, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Key")
- return
- }
- if zb0003 > encodedKVRecordV6MaxKeyLength {
- err = msgp.ErrOverflow(uint64(zb0003), uint64(encodedKVRecordV6MaxKeyLength))
- return
- }
- (*z).Key, bts, err = msgp.ReadBytesBytes(bts, (*z).Key)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Key")
- return
- }
- }
- if zb0001 > 0 {
- zb0001--
- var zb0004 int
- zb0004, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Value")
- return
- }
- if zb0004 > encodedKVRecordV6MaxValueLength {
- err = msgp.ErrOverflow(uint64(zb0004), uint64(encodedKVRecordV6MaxValueLength))
- return
- }
- (*z).Value, bts, err = msgp.ReadBytesBytes(bts, (*z).Value)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array", "Value")
- return
- }
- }
- if zb0001 > 0 {
- err = msgp.ErrTooManyArrayFields(zb0001)
- if err != nil {
- err = msgp.WrapError(err, "struct-from-array")
- return
- }
- }
- } else {
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- if zb0002 {
- (*z) = encodedKVRecordV6{}
- }
- for zb0001 > 0 {
- zb0001--
- field, bts, err = msgp.ReadMapKeyZC(bts)
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- switch string(field) {
- case "k":
- var zb0005 int
- zb0005, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "Key")
- return
- }
- if zb0005 > encodedKVRecordV6MaxKeyLength {
- err = msgp.ErrOverflow(uint64(zb0005), uint64(encodedKVRecordV6MaxKeyLength))
- return
- }
- (*z).Key, bts, err = msgp.ReadBytesBytes(bts, (*z).Key)
- if err != nil {
- err = msgp.WrapError(err, "Key")
- return
- }
- case "v":
- var zb0006 int
- zb0006, err = msgp.ReadBytesBytesHeader(bts)
- if err != nil {
- err = msgp.WrapError(err, "Value")
- return
- }
- if zb0006 > encodedKVRecordV6MaxValueLength {
- err = msgp.ErrOverflow(uint64(zb0006), uint64(encodedKVRecordV6MaxValueLength))
- return
- }
- (*z).Value, bts, err = msgp.ReadBytesBytes(bts, (*z).Value)
- if err != nil {
- err = msgp.WrapError(err, "Value")
- return
- }
- default:
- err = msgp.ErrNoField(string(field))
- if err != nil {
- err = msgp.WrapError(err)
- return
- }
- }
- }
- }
- o = bts
- return
-}
-
-func (_ *encodedKVRecordV6) CanUnmarshalMsg(z interface{}) bool {
- _, ok := (z).(*encodedKVRecordV6)
- return ok
-}
-
-// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
-func (z *encodedKVRecordV6) Msgsize() (s int) {
- s = 1 + 2 + msgp.BytesPrefixSize + len((*z).Key) + 2 + msgp.BytesPrefixSize + len((*z).Value)
- return
-}
-
-// MsgIsZero returns whether this is a zero value
-func (z *encodedKVRecordV6) MsgIsZero() bool {
- return (len((*z).Key) == 0) && (len((*z).Value) == 0)
-}
diff --git a/ledger/msgp_gen_test.go b/ledger/msgp_gen_test.go
index e7739ad97..de29b5f11 100644
--- a/ledger/msgp_gen_test.go
+++ b/ledger/msgp_gen_test.go
@@ -193,183 +193,3 @@ func BenchmarkUnmarshalcatchpointFileChunkV6(b *testing.B) {
}
}
}
-
-func TestMarshalUnmarshalencodedBalanceRecordV5(t *testing.T) {
- partitiontest.PartitionTest(t)
- v := encodedBalanceRecordV5{}
- bts := v.MarshalMsg(nil)
- left, err := v.UnmarshalMsg(bts)
- if err != nil {
- t.Fatal(err)
- }
- if len(left) > 0 {
- t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
- }
-
- left, err = msgp.Skip(bts)
- if err != nil {
- t.Fatal(err)
- }
- if len(left) > 0 {
- t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
- }
-}
-
-func TestRandomizedEncodingencodedBalanceRecordV5(t *testing.T) {
- protocol.RunEncodingTest(t, &encodedBalanceRecordV5{})
-}
-
-func BenchmarkMarshalMsgencodedBalanceRecordV5(b *testing.B) {
- v := encodedBalanceRecordV5{}
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- v.MarshalMsg(nil)
- }
-}
-
-func BenchmarkAppendMsgencodedBalanceRecordV5(b *testing.B) {
- v := encodedBalanceRecordV5{}
- bts := make([]byte, 0, v.Msgsize())
- bts = v.MarshalMsg(bts[0:0])
- b.SetBytes(int64(len(bts)))
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- bts = v.MarshalMsg(bts[0:0])
- }
-}
-
-func BenchmarkUnmarshalencodedBalanceRecordV5(b *testing.B) {
- v := encodedBalanceRecordV5{}
- bts := v.MarshalMsg(nil)
- b.ReportAllocs()
- b.SetBytes(int64(len(bts)))
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- _, err := v.UnmarshalMsg(bts)
- if err != nil {
- b.Fatal(err)
- }
- }
-}
-
-func TestMarshalUnmarshalencodedBalanceRecordV6(t *testing.T) {
- partitiontest.PartitionTest(t)
- v := encodedBalanceRecordV6{}
- bts := v.MarshalMsg(nil)
- left, err := v.UnmarshalMsg(bts)
- if err != nil {
- t.Fatal(err)
- }
- if len(left) > 0 {
- t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
- }
-
- left, err = msgp.Skip(bts)
- if err != nil {
- t.Fatal(err)
- }
- if len(left) > 0 {
- t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
- }
-}
-
-func TestRandomizedEncodingencodedBalanceRecordV6(t *testing.T) {
- protocol.RunEncodingTest(t, &encodedBalanceRecordV6{})
-}
-
-func BenchmarkMarshalMsgencodedBalanceRecordV6(b *testing.B) {
- v := encodedBalanceRecordV6{}
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- v.MarshalMsg(nil)
- }
-}
-
-func BenchmarkAppendMsgencodedBalanceRecordV6(b *testing.B) {
- v := encodedBalanceRecordV6{}
- bts := make([]byte, 0, v.Msgsize())
- bts = v.MarshalMsg(bts[0:0])
- b.SetBytes(int64(len(bts)))
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- bts = v.MarshalMsg(bts[0:0])
- }
-}
-
-func BenchmarkUnmarshalencodedBalanceRecordV6(b *testing.B) {
- v := encodedBalanceRecordV6{}
- bts := v.MarshalMsg(nil)
- b.ReportAllocs()
- b.SetBytes(int64(len(bts)))
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- _, err := v.UnmarshalMsg(bts)
- if err != nil {
- b.Fatal(err)
- }
- }
-}
-
-func TestMarshalUnmarshalencodedKVRecordV6(t *testing.T) {
- partitiontest.PartitionTest(t)
- v := encodedKVRecordV6{}
- bts := v.MarshalMsg(nil)
- left, err := v.UnmarshalMsg(bts)
- if err != nil {
- t.Fatal(err)
- }
- if len(left) > 0 {
- t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
- }
-
- left, err = msgp.Skip(bts)
- if err != nil {
- t.Fatal(err)
- }
- if len(left) > 0 {
- t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
- }
-}
-
-func TestRandomizedEncodingencodedKVRecordV6(t *testing.T) {
- protocol.RunEncodingTest(t, &encodedKVRecordV6{})
-}
-
-func BenchmarkMarshalMsgencodedKVRecordV6(b *testing.B) {
- v := encodedKVRecordV6{}
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- v.MarshalMsg(nil)
- }
-}
-
-func BenchmarkAppendMsgencodedKVRecordV6(b *testing.B) {
- v := encodedKVRecordV6{}
- bts := make([]byte, 0, v.Msgsize())
- bts = v.MarshalMsg(bts[0:0])
- b.SetBytes(int64(len(bts)))
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- bts = v.MarshalMsg(bts[0:0])
- }
-}
-
-func BenchmarkUnmarshalencodedKVRecordV6(b *testing.B) {
- v := encodedKVRecordV6{}
- bts := v.MarshalMsg(nil)
- b.ReportAllocs()
- b.SetBytes(int64(len(bts)))
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- _, err := v.UnmarshalMsg(bts)
- if err != nil {
- b.Fatal(err)
- }
- }
-}
diff --git a/ledger/notifier.go b/ledger/notifier.go
index b7d220ac3..b9660abf9 100644
--- a/ledger/notifier.go
+++ b/ledger/notifier.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -28,11 +28,6 @@ import (
"github.com/algorand/go-algorand/ledger/ledgercore"
)
-// BlockListener represents an object that needs to get notified on new blocks.
-type BlockListener interface {
- OnNewBlock(block bookkeeping.Block, delta ledgercore.StateDelta)
-}
-
type blockDeltaPair struct {
block bookkeeping.Block
delta ledgercore.StateDelta
@@ -41,7 +36,7 @@ type blockDeltaPair struct {
type blockNotifier struct {
mu deadlock.Mutex
cond *sync.Cond
- listeners []BlockListener
+ listeners []ledgercore.BlockListener
pendingBlocks []blockDeltaPair
running bool
// closing is the waitgroup used to synchronize closing the worker goroutine. It's being increased during loadFromDisk, and the worker is responsible to call Done on it once it's aborting it's goroutine. The close function waits on this to complete.
@@ -96,7 +91,7 @@ func (bn *blockNotifier) loadFromDisk(l ledgerForTracker, _ basics.Round) error
return nil
}
-func (bn *blockNotifier) register(listeners []BlockListener) {
+func (bn *blockNotifier) register(listeners []ledgercore.BlockListener) {
bn.mu.Lock()
defer bn.mu.Unlock()
diff --git a/ledger/onlineaccountscache.go b/ledger/onlineaccountscache.go
index 4d6c97602..e983ff024 100644
--- a/ledger/onlineaccountscache.go
+++ b/ledger/onlineaccountscache.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/onlineaccountscache_test.go b/ledger/onlineaccountscache_test.go
index 23010b35d..6cb97f610 100644
--- a/ledger/onlineaccountscache_test.go
+++ b/ledger/onlineaccountscache_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/onlinetopheap.go b/ledger/onlinetopheap.go
index c7182b9cd..6d3ba155d 100644
--- a/ledger/onlinetopheap.go
+++ b/ledger/onlinetopheap.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/onlinetopheap_test.go b/ledger/onlinetopheap_test.go
index 7d72d0d41..10a068aa9 100644
--- a/ledger/onlinetopheap_test.go
+++ b/ledger/onlinetopheap_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/perf_test.go b/ledger/perf_test.go
index 50c407c67..68e9ab76c 100644
--- a/ledger/perf_test.go
+++ b/ledger/perf_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/persistedaccts_list.go b/ledger/persistedaccts_list.go
index c7c884a86..f29f2857d 100644
--- a/ledger/persistedaccts_list.go
+++ b/ledger/persistedaccts_list.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/persistedaccts_list_test.go b/ledger/persistedaccts_list_test.go
index 9c7b0d69c..41881520c 100644
--- a/ledger/persistedaccts_list_test.go
+++ b/ledger/persistedaccts_list_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/persistedkvs.go b/ledger/persistedkvs.go
index 34f3c36ec..b97234f34 100644
--- a/ledger/persistedkvs.go
+++ b/ledger/persistedkvs.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/persistedkvs_test.go b/ledger/persistedkvs_test.go
index eb5ed9dff..5d9620d27 100644
--- a/ledger/persistedkvs_test.go
+++ b/ledger/persistedkvs_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/persistedonlineaccts_list.go b/ledger/persistedonlineaccts_list.go
index e5775498d..9a00695ab 100644
--- a/ledger/persistedonlineaccts_list.go
+++ b/ledger/persistedonlineaccts_list.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/persistedonlineaccts_list_test.go b/ledger/persistedonlineaccts_list_test.go
index 7e4a10721..56184fd2e 100644
--- a/ledger/persistedonlineaccts_list_test.go
+++ b/ledger/persistedonlineaccts_list_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/persistedresources_list.go b/ledger/persistedresources_list.go
index baa7ac351..2bf9cb6a2 100644
--- a/ledger/persistedresources_list.go
+++ b/ledger/persistedresources_list.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/persistedresources_list_test.go b/ledger/persistedresources_list_test.go
index 5261eb42c..484af1395 100644
--- a/ledger/persistedresources_list_test.go
+++ b/ledger/persistedresources_list_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/roundlru.go b/ledger/roundlru.go
index cfc4675ee..43d863987 100644
--- a/ledger/roundlru.go
+++ b/ledger/roundlru.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/roundlru_test.go b/ledger/roundlru_test.go
index ef8ac03af..8033f3439 100644
--- a/ledger/roundlru_test.go
+++ b/ledger/roundlru_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/simple_test.go b/ledger/simple_test.go
index 22781c70f..e934e3f01 100644
--- a/ledger/simple_test.go
+++ b/ledger/simple_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -116,7 +116,7 @@ func txgroup(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator, txns .
for _, txn := range txns {
fillDefaults(t, ledger, eval, txn)
}
- txgroup := txntest.SignedTxns(txns...)
+ txgroup := txntest.Group(txns...)
return eval.TransactionGroup(transactions.WrapSignedTxnsWithAD(txgroup))
}
diff --git a/ledger/simulation/simulator.go b/ledger/simulation/simulator.go
new file mode 100644
index 000000000..b559e989f
--- /dev/null
+++ b/ledger/simulation/simulator.go
@@ -0,0 +1,217 @@
+// 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 simulation
+
+import (
+ "errors"
+
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/data"
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/data/bookkeeping"
+ "github.com/algorand/go-algorand/data/transactions"
+ "github.com/algorand/go-algorand/data/transactions/logic"
+ "github.com/algorand/go-algorand/data/transactions/verify"
+ "github.com/algorand/go-algorand/ledger/ledgercore"
+ "github.com/algorand/go-algorand/protocol"
+)
+
+// ==============================
+// > Simulator Ledger
+// ==============================
+
+// simulatorLedger patches the ledger interface to use a constant latest round.
+type simulatorLedger struct {
+ *data.Ledger
+ start basics.Round
+}
+
+// Latest is part of the LedgerForSimulator interface.
+// We override this to use the set latest to prevent racing with the network
+func (l simulatorLedger) Latest() basics.Round {
+ return l.start
+}
+
+// LookupLatest would implicitly use the latest round in the _underlying_
+// Ledger, it would give wrong results if that ledger has moved forward. But it
+// should never be called, as the REST API is the only code using this function,
+// and the REST API should never have access to a simulatorLedger.
+func (l simulatorLedger) LookupLatest(addr basics.Address) (basics.AccountData, basics.Round, basics.MicroAlgos, error) {
+ err := errors.New("unexpected call to LookupLatest")
+ return basics.AccountData{}, 0, basics.MicroAlgos{}, err
+}
+
+// ==============================
+// > Simulator Tracer
+// ==============================
+
+type evalTracer struct {
+ logic.NullEvalTracer
+}
+
+func makeTracer() logic.EvalTracer {
+ return &evalTracer{}
+}
+
+// ==============================
+// > Simulator Errors
+// ==============================
+
+// SimulatorError is the base error type for all simulator errors.
+type SimulatorError struct {
+ err error
+}
+
+func (s SimulatorError) Error() string {
+ return s.err.Error()
+}
+
+func (s SimulatorError) Unwrap() error {
+ return s.err
+}
+
+// InvalidTxGroupError occurs when an invalid transaction group was submitted to the simulator.
+type InvalidTxGroupError struct {
+ SimulatorError
+}
+
+// EvalFailureError represents an error that occurred during evaluation.
+type EvalFailureError struct {
+ SimulatorError
+}
+
+// ==============================
+// > Simulator
+// ==============================
+
+// Simulator is a transaction group simulator for the block evaluator.
+type Simulator struct {
+ ledger simulatorLedger
+}
+
+// MakeSimulator creates a new simulator from a ledger.
+func MakeSimulator(ledger *data.Ledger) *Simulator {
+ return &Simulator{
+ ledger: simulatorLedger{ledger, ledger.Latest()},
+ }
+}
+
+func txnHasNoSignature(txn transactions.SignedTxn) bool {
+ return txn.Sig.Blank() && txn.Msig.Blank() && txn.Lsig.Blank()
+}
+
+// A randomly generated private key. The actual value does not matter, as long as this is a valid
+// private key.
+var proxySigner = crypto.PrivateKey{
+ 128, 128, 92, 23, 212, 119, 175, 51, 157, 2, 165,
+ 215, 137, 37, 82, 42, 52, 227, 54, 41, 243, 67,
+ 141, 76, 208, 17, 199, 17, 140, 46, 113, 0, 159,
+ 50, 105, 52, 77, 104, 118, 200, 104, 220, 105, 21,
+ 147, 162, 191, 236, 115, 201, 197, 128, 8, 91, 224,
+ 78, 104, 209, 2, 185, 110, 28, 42, 97,
+}
+
+// check verifies that the transaction is well-formed and has valid or missing signatures.
+// An invalid transaction group error is returned if the transaction is not well-formed or there are invalid signatures.
+// To make things easier, we support submitting unsigned transactions and will respond whether signatures are missing.
+func (s Simulator) check(hdr bookkeeping.BlockHeader, txgroup []transactions.SignedTxn, debugger logic.EvalTracer) (bool, error) {
+ proxySignerSecrets, err := crypto.SecretKeyToSignatureSecrets(proxySigner)
+ if err != nil {
+ return false, err
+ }
+
+ // Find and prep any transactions that are missing signatures. We will modify a copy of these
+ // transactions to pass signature verification. The modifications will not affect the input
+ // txgroup slice.
+ //
+ // Note: currently we only support missing transaction signatures, but it should be possible to
+ // support unsigned delegated LogicSigs as well. A single-signature unsigned delegated LogicSig
+ // is indistinguishable from an escrow LogicSig, so we would need to decide on another way of
+ // denoting that a LogicSig's delegation signature is omitted, e.g. by setting all the bits of
+ // the signature.
+ missingSigs := make([]int, 0, len(txgroup))
+ txnsToVerify := make([]transactions.SignedTxn, len(txgroup))
+ for i, stxn := range txgroup {
+ if stxn.Txn.Type == protocol.StateProofTx {
+ return false, errors.New("cannot simulate StateProof transactions")
+ }
+ if txnHasNoSignature(stxn) {
+ missingSigs = append(missingSigs, i)
+
+ // Replace the signed txn with one signed by the proxySigner. At evaluation this would
+ // raise an error, since the proxySigner's public key likely does not have authority
+ // over the sender's account. However, this will pass validation, since the signature
+ // itself is valid.
+ txnsToVerify[i] = stxn.Txn.Sign(proxySignerSecrets)
+ } else {
+ txnsToVerify[i] = stxn
+ }
+ }
+
+ // Verify the signed transactions are well-formed and have valid signatures
+ _, err = verify.TxnGroupWithTracer(txnsToVerify, &hdr, nil, s.ledger, debugger)
+ if err != nil {
+ return false, InvalidTxGroupError{SimulatorError{err}}
+ }
+
+ return len(missingSigs) != 0, nil
+}
+
+func (s Simulator) evaluate(hdr bookkeeping.BlockHeader, stxns []transactions.SignedTxn, tracer logic.EvalTracer) (*ledgercore.ValidatedBlock, error) {
+ // s.ledger has 'StartEvaluator' because *data.Ledger is embedded in the simulatorLedger
+ // and data.Ledger embeds *ledger.Ledger
+ eval, err := s.ledger.StartEvaluator(hdr, len(stxns), 0)
+ if err != nil {
+ return nil, err
+ }
+ eval.Tracer = tracer
+
+ group := transactions.WrapSignedTxnsWithAD(stxns)
+
+ err = eval.TransactionGroup(group)
+ if err != nil {
+ return nil, EvalFailureError{SimulatorError{err}}
+ }
+
+ // Finally, process any pending end-of-block state changes.
+ vb, err := eval.GenerateBlock()
+ if err != nil {
+ return nil, err
+ }
+
+ return vb, nil
+}
+
+// Simulate simulates a transaction group using the simulator. Will error if the transaction group is not well-formed.
+func (s Simulator) Simulate(txgroup []transactions.SignedTxn) (*ledgercore.ValidatedBlock, bool, error) {
+ prevBlockHdr, err := s.ledger.BlockHdr(s.ledger.start)
+ if err != nil {
+ return nil, false, err
+ }
+ nextBlock := bookkeeping.MakeBlock(prevBlockHdr)
+ hdr := nextBlock.BlockHeader
+ simulatorTracer := makeTracer()
+
+ // check that the transaction is well-formed and mark whether signatures are missing
+ missingSignatures, err := s.check(hdr, txgroup, simulatorTracer)
+ if err != nil {
+ return nil, false, err
+ }
+
+ vb, err := s.evaluate(hdr, txgroup, simulatorTracer)
+ return vb, missingSignatures, err
+}
diff --git a/ledger/simulation/simulator_test.go b/ledger/simulation/simulator_test.go
new file mode 100644
index 000000000..d02910919
--- /dev/null
+++ b/ledger/simulation/simulator_test.go
@@ -0,0 +1,746 @@
+// 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 simulation_test
+
+import (
+ "encoding/binary"
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/algorand/go-algorand/config"
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/data"
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/data/transactions"
+ "github.com/algorand/go-algorand/data/transactions/logic"
+
+ "github.com/algorand/go-algorand/ledger"
+ "github.com/algorand/go-algorand/ledger/internal"
+ "github.com/algorand/go-algorand/ledger/simulation"
+ ledgertesting "github.com/algorand/go-algorand/ledger/testing"
+ "github.com/algorand/go-algorand/libgoal"
+ "github.com/algorand/go-algorand/logging"
+ "github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/go-algorand/test/partitiontest"
+ "github.com/stretchr/testify/require"
+)
+
+// ==============================
+// > Simulation Test Helpers
+// ==============================
+
+type account struct {
+ addr basics.Address
+ sk *crypto.SignatureSecrets
+ acctData basics.AccountData
+}
+
+func prepareSimulatorTest(t *testing.T) (l *data.Ledger, accounts []account, makeTxnHeader func(sender basics.Address) transactions.Header) {
+ genesisInitState, keys := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
+
+ // Prepare ledger
+ const inMem = true
+ cfg := config.GetDefaultLocal()
+ cfg.Archival = true
+ log := logging.TestingLog(t)
+ log.SetLevel(logging.Warn)
+ realLedger, err := ledger.OpenLedger(log, t.Name(), inMem, genesisInitState, cfg)
+ require.NoError(t, err, "could not open ledger")
+
+ l = &data.Ledger{Ledger: realLedger}
+ require.NotNil(t, l)
+
+ // Reformat accounts
+ accounts = make([]account, len(keys)-2) // -2 for pool and sink accounts
+ i := 0
+ for addr, key := range keys {
+ if addr == ledgertesting.PoolAddr() || addr == ledgertesting.SinkAddr() {
+ continue
+ }
+
+ acctData := genesisInitState.Accounts[addr]
+ accounts[i] = account{addr, key, acctData}
+ i++
+ }
+
+ // txn header generator
+ hdr, err := l.BlockHdr(l.Latest())
+ require.NoError(t, err)
+ makeTxnHeader = func(sender basics.Address) transactions.Header {
+ return transactions.Header{
+ Fee: basics.MicroAlgos{Raw: 1000},
+ FirstValid: hdr.Round,
+ GenesisID: hdr.GenesisID,
+ GenesisHash: hdr.GenesisHash,
+ LastValid: hdr.Round + basics.Round(1000),
+ Note: []byte{240, 134, 38, 55, 197, 14, 142, 132},
+ Sender: sender,
+ }
+ }
+
+ return
+}
+
+func makeTestClient() libgoal.Client {
+ c, err := libgoal.MakeClientFromConfig(libgoal.ClientConfig{
+ AlgodDataDir: "NO_DIR",
+ }, libgoal.DynamicClient)
+ if err != nil {
+ panic(err)
+ }
+
+ return c
+}
+
+// Attach group ID to a transaction group. Mutates the group directly.
+func attachGroupID(txgroup []transactions.SignedTxn) error {
+ txnArray := make([]transactions.Transaction, len(txgroup))
+ for i, txn := range txgroup {
+ txnArray[i] = txn.Txn
+ }
+
+ client := makeTestClient()
+ groupID, err := client.GroupID(txnArray)
+ if err != nil {
+ return err
+ }
+
+ for i := range txgroup {
+ txgroup[i].Txn.Header.Group = groupID
+ }
+
+ return nil
+}
+
+func uint64ToBytes(num uint64) []byte {
+ ibytes := make([]byte, 8)
+ binary.BigEndian.PutUint64(ibytes, num)
+ return ibytes
+}
+
+// ==============================
+// > Sanity Tests
+// ==============================
+
+// We want to be careful that the Algod ledger does not move on to another round
+// so we confirm here that all ledger methods which implicitly access the current round
+// are overriden within the `simulatorLedger`.
+func TestNonOverridenDataLedgerMethodsUseRoundParameter(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, _, _ := prepareSimulatorTest(t)
+
+ // methods overriden by `simulatorLedger``
+ overridenMethods := []string{
+ "Latest",
+ "LookupLatest",
+ }
+
+ // methods that don't use a round number
+ excludedMethods := []string{
+ "GenesisHash",
+ "GenesisProto",
+ "LatestTotals",
+ "FlushCaches",
+ }
+
+ methodIsSkipped := func(methodName string) bool {
+ for _, overridenMethod := range overridenMethods {
+ if overridenMethod == methodName {
+ return true
+ }
+ }
+ for _, excludedMethod := range excludedMethods {
+ if excludedMethod == methodName {
+ return true
+ }
+ }
+ return false
+ }
+
+ methodExistsInEvalLedger := func(methodName string) bool {
+ evalLedgerType := reflect.TypeOf((*internal.LedgerForEvaluator)(nil)).Elem()
+ for i := 0; i < evalLedgerType.NumMethod(); i++ {
+ if evalLedgerType.Method(i).Name == methodName {
+ return true
+ }
+ }
+ return false
+ }
+
+ methodHasRoundParameter := func(methodType reflect.Type) bool {
+ for i := 0; i < methodType.NumIn(); i++ {
+ if methodType.In(i) == reflect.TypeOf(basics.Round(0)) {
+ return true
+ }
+ }
+ return false
+ }
+
+ ledgerType := reflect.TypeOf(l)
+ for i := 0; i < ledgerType.NumMethod(); i++ {
+ method := ledgerType.Method(i)
+ if methodExistsInEvalLedger(method.Name) && !methodIsSkipped(method.Name) {
+ require.True(t, methodHasRoundParameter(method.Type), "method %s has no round parameter", method.Name)
+ }
+ }
+}
+
+// ==============================
+// > Simulation Tests
+// ==============================
+
+func TestPayTxn(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ for _, signed := range []bool{true, false} {
+ signed := signed
+ t.Run(fmt.Sprintf("signed=%t", signed), func(t *testing.T) {
+ t.Parallel()
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender := accounts[0]
+
+ txn := transactions.SignedTxn{
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: makeTxnHeader(sender.addr),
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: sender.addr,
+ Amount: basics.MicroAlgos{Raw: 0},
+ },
+ },
+ }
+
+ if signed {
+ txn = txn.Txn.Sign(sender.sk)
+ }
+
+ txgroup := []transactions.SignedTxn{txn}
+
+ _, _, err := s.Simulate(txgroup)
+ require.NoError(t, err)
+ })
+ }
+}
+
+func TestOverspendPayTxn(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ for _, signed := range []bool{true, false} {
+ signed := signed
+ t.Run(fmt.Sprintf("signed=%t", signed), func(t *testing.T) {
+ t.Parallel()
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender := accounts[0]
+ senderBalance := sender.acctData.MicroAlgos
+ amount := senderBalance.Raw + 100
+
+ txn := transactions.SignedTxn{
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: makeTxnHeader(sender.addr),
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: sender.addr,
+ Amount: basics.MicroAlgos{Raw: amount}, // overspend
+ },
+ },
+ }
+
+ if signed {
+ txn = txn.Txn.Sign(sender.sk)
+ }
+
+ txgroup := []transactions.SignedTxn{txn}
+
+ _, _, err := s.Simulate(txgroup)
+ require.ErrorAs(t, err, &simulation.EvalFailureError{})
+ require.ErrorContains(t, err, fmt.Sprintf("tried to spend {%d}", amount))
+ })
+ }
+}
+
+func TestAuthAddrTxn(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ for _, signed := range []bool{true, false} {
+ signed := signed
+ t.Run(fmt.Sprintf("signed=%t", signed), func(t *testing.T) {
+ t.Parallel()
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender := accounts[0]
+ authority := accounts[1]
+
+ txn := transactions.SignedTxn{
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: makeTxnHeader(sender.addr),
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: sender.addr,
+ Amount: basics.MicroAlgos{Raw: 0},
+ },
+ },
+ AuthAddr: authority.addr,
+ }
+
+ if signed {
+ txn = txn.Txn.Sign(authority.sk)
+ }
+
+ txgroup := []transactions.SignedTxn{txn}
+
+ _, _, err := s.Simulate(txgroup)
+ require.ErrorContains(t, err, fmt.Sprintf("should have been authorized by %s but was actually authorized by %s", sender.addr, authority.addr))
+ })
+ }
+}
+
+func TestStateProofTxn(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, _, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+
+ txgroup := []transactions.SignedTxn{
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.StateProofTx,
+ Header: makeTxnHeader(transactions.StateProofSender),
+ // No need to fill out StateProofTxnFields, this should fail at signature verification
+ },
+ },
+ }
+
+ _, _, err := s.Simulate(txgroup)
+ require.ErrorContains(t, err, "cannot simulate StateProof transactions")
+}
+
+func TestSimpleGroupTxn(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender1 := accounts[0].addr
+ sender1Balance := accounts[0].acctData.MicroAlgos
+ sender2 := accounts[1].addr
+ sender2Balance := accounts[1].acctData.MicroAlgos
+
+ // Send money back and forth
+ txgroup := []transactions.SignedTxn{
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: makeTxnHeader(sender1),
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: sender2,
+ Amount: basics.MicroAlgos{Raw: 1000000},
+ },
+ },
+ },
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: makeTxnHeader(sender2),
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: sender1,
+ Amount: basics.MicroAlgos{Raw: 0},
+ },
+ },
+ },
+ }
+
+ // Should fail if there is no group parameter
+ _, _, err := s.Simulate(txgroup)
+ require.ErrorAs(t, err, &simulation.EvalFailureError{})
+ require.ErrorContains(t, err, "had zero Group but was submitted in a group of 2")
+
+ // Add group parameter
+ err = attachGroupID(txgroup)
+ require.NoError(t, err)
+
+ // Check balances before transaction
+ sender1Data, _, err := l.LookupWithoutRewards(l.Latest(), sender1)
+ require.NoError(t, err)
+ require.Equal(t, sender1Balance, sender1Data.MicroAlgos)
+
+ sender2Data, _, err := l.LookupWithoutRewards(l.Latest(), sender2)
+ require.NoError(t, err)
+ require.Equal(t, sender2Balance, sender2Data.MicroAlgos)
+
+ // Should now pass
+ _, _, err = s.Simulate(txgroup)
+ require.NoError(t, err)
+
+ // Confirm balances have not changed
+ sender1Data, _, err = l.LookupWithoutRewards(l.Latest(), sender1)
+ require.NoError(t, err)
+ require.Equal(t, sender1Balance, sender1Data.MicroAlgos)
+
+ sender2Data, _, err = l.LookupWithoutRewards(l.Latest(), sender2)
+ require.NoError(t, err)
+ require.Equal(t, sender2Balance, sender2Data.MicroAlgos)
+}
+
+const trivialAVMProgram = `#pragma version 2
+int 1`
+const rejectAVMProgram = `#pragma version 2
+int 0`
+
+func TestSimpleAppCall(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender := accounts[0].addr
+
+ // Compile AVM program
+ ops, err := logic.AssembleString(trivialAVMProgram)
+ require.NoError(t, err, ops.Errors)
+ prog := ops.Program
+
+ // Create program and call it
+ futureAppID := 1
+ txgroup := []transactions.SignedTxn{
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.ApplicationCallTx,
+ Header: makeTxnHeader(sender),
+ ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
+ ApplicationID: 0,
+ ApprovalProgram: prog,
+ ClearStateProgram: prog,
+ LocalStateSchema: basics.StateSchema{
+ NumUint: 0,
+ NumByteSlice: 0,
+ },
+ GlobalStateSchema: basics.StateSchema{
+ NumUint: 0,
+ NumByteSlice: 0,
+ },
+ },
+ },
+ },
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.ApplicationCallTx,
+ Header: makeTxnHeader(sender),
+ ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
+ ApplicationID: basics.AppIndex(futureAppID),
+ },
+ },
+ },
+ }
+
+ err = attachGroupID(txgroup)
+ require.NoError(t, err)
+
+ _, _, err = s.Simulate(txgroup)
+ require.NoError(t, err)
+}
+
+func TestRejectAppCall(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender := accounts[0].addr
+
+ // Compile AVM program
+ ops, err := logic.AssembleString(rejectAVMProgram)
+ require.NoError(t, err, ops.Errors)
+ prog := ops.Program
+
+ // Create program and call it
+ txgroup := []transactions.SignedTxn{
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.ApplicationCallTx,
+ Header: makeTxnHeader(sender),
+ ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
+ ApplicationID: 0,
+ ApprovalProgram: prog,
+ ClearStateProgram: prog,
+ LocalStateSchema: basics.StateSchema{
+ NumUint: 0,
+ NumByteSlice: 0,
+ },
+ GlobalStateSchema: basics.StateSchema{
+ NumUint: 0,
+ NumByteSlice: 0,
+ },
+ },
+ },
+ },
+ }
+
+ err = attachGroupID(txgroup)
+ require.NoError(t, err)
+
+ _, _, err = s.Simulate(txgroup)
+ require.ErrorAs(t, err, &simulation.EvalFailureError{})
+ require.ErrorContains(t, err, "transaction rejected by ApprovalProgram")
+}
+
+func TestSignatureCheck(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender := accounts[0].addr
+
+ txgroup := []transactions.SignedTxn{
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: makeTxnHeader(sender),
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: sender,
+ Amount: basics.MicroAlgos{Raw: 0},
+ },
+ },
+ },
+ }
+
+ // should catch missing signature
+ _, missingSignatures, err := s.Simulate(txgroup)
+ require.NoError(t, err)
+ require.True(t, missingSignatures)
+
+ // add signature
+ signatureSecrets := accounts[0].sk
+ txgroup[0] = txgroup[0].Txn.Sign(signatureSecrets)
+
+ // should not error now that we have a signature
+ _, missingSignatures, err = s.Simulate(txgroup)
+ require.NoError(t, err)
+ require.False(t, missingSignatures)
+
+ // should error with invalid signature
+ txgroup[0].Sig[0] += byte(1) // will wrap if > 255
+ _, _, err = s.Simulate(txgroup)
+ require.ErrorAs(t, err, &simulation.InvalidTxGroupError{})
+ require.ErrorContains(t, err, "one signature didn't pass")
+}
+
+// TestInvalidTxGroup tests that a transaction group with invalid transactions
+// is rejected by the simulator as an InvalidTxGroupError instead of a EvalFailureError.
+func TestInvalidTxGroup(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ receiver := accounts[0].addr
+
+ txgroup := []transactions.SignedTxn{
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ // invalid sender
+ Header: makeTxnHeader(ledgertesting.PoolAddr()),
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: receiver,
+ Amount: basics.MicroAlgos{Raw: 0},
+ },
+ },
+ },
+ }
+
+ // should error with invalid transaction group error
+ _, _, err := s.Simulate(txgroup)
+ require.ErrorAs(t, err, &simulation.InvalidTxGroupError{})
+ require.ErrorContains(t, err, "transaction from incentive pool is invalid")
+}
+
+const accountBalanceCheckProgram = `#pragma version 4
+ txn ApplicationID // [appId]
+ bz end // []
+ int 1 // [1]
+ balance // [bal[1]]
+ itob // [itob(bal[1])]
+ txn ApplicationArgs 0 // [itob(bal[1]), args[0]]
+ == // [itob(bal[1])=?=args[0]]
+ assert
+ b end
+end:
+ int 1 // [1]
+`
+
+func TestBalanceChangesWithApp(t *testing.T) {
+ // Send a payment transaction to a new account and confirm its balance within an app call
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender := accounts[0].addr
+ senderBalance := accounts[0].acctData.MicroAlgos.Raw
+ sendAmount := senderBalance - 500000
+ receiver := accounts[1].addr
+ receiverBalance := accounts[1].acctData.MicroAlgos.Raw
+
+ // Compile approval program
+ ops, err := logic.AssembleString(accountBalanceCheckProgram)
+ require.NoError(t, err, ops.Errors)
+ approvalProg := ops.Program
+
+ // Compile clear program
+ ops, err = logic.AssembleString(trivialAVMProgram)
+ require.NoError(t, err, ops.Errors)
+ clearStateProg := ops.Program
+
+ futureAppID := 1
+ txgroup := []transactions.SignedTxn{
+ // create app
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.ApplicationCallTx,
+ Header: makeTxnHeader(sender),
+ ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
+ ApplicationID: 0,
+ ApprovalProgram: approvalProg,
+ ClearStateProgram: clearStateProg,
+ LocalStateSchema: basics.StateSchema{
+ NumUint: 0,
+ NumByteSlice: 0,
+ },
+ GlobalStateSchema: basics.StateSchema{
+ NumUint: 0,
+ NumByteSlice: 0,
+ },
+ },
+ },
+ },
+ // check balance
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.ApplicationCallTx,
+ Header: makeTxnHeader(sender),
+ ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
+ ApplicationID: basics.AppIndex(futureAppID),
+ Accounts: []basics.Address{receiver},
+ ApplicationArgs: [][]byte{uint64ToBytes(receiverBalance)},
+ },
+ },
+ },
+ // send payment
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: makeTxnHeader(sender),
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: receiver,
+ Amount: basics.MicroAlgos{Raw: sendAmount},
+ },
+ },
+ },
+ // check balance changed
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.ApplicationCallTx,
+ Header: makeTxnHeader(sender),
+ ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
+ ApplicationID: basics.AppIndex(futureAppID),
+ Accounts: []basics.Address{receiver},
+ ApplicationArgs: [][]byte{uint64ToBytes(receiverBalance + sendAmount)},
+ },
+ },
+ },
+ }
+
+ err = attachGroupID(txgroup)
+ require.NoError(t, err)
+
+ _, _, err = s.Simulate(txgroup)
+ require.NoError(t, err)
+}
+
+// TestBalanceChangesWithApp tests that the simulator's transaction group checks
+// allow for pooled fees across a mix of signed and unsigned transactions.
+// Transaction 1 is a signed transaction with not enough fees paid on its own.
+// Transaction 2 is an unsigned transaction with enough fees paid to cover transaction 1.
+func TestPooledFeesAcrossSignedAndUnsigned(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ l, accounts, makeTxnHeader := prepareSimulatorTest(t)
+ defer l.Close()
+ s := simulation.MakeSimulator(l)
+ sender1 := accounts[0].addr
+ sender2 := accounts[1].addr
+
+ txnHeader1 := makeTxnHeader(sender1)
+ txnHeader2 := makeTxnHeader(sender2)
+ txnHeader1.Fee = basics.MicroAlgos{Raw: txnHeader1.Fee.Raw - 100}
+ txnHeader2.Fee = basics.MicroAlgos{Raw: txnHeader2.Fee.Raw + 100}
+
+ // Send money back and forth
+ txgroup := []transactions.SignedTxn{
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: txnHeader1,
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: sender2,
+ Amount: basics.MicroAlgos{Raw: 1000000},
+ },
+ },
+ },
+ {
+ Txn: transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: txnHeader2,
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: sender1,
+ Amount: basics.MicroAlgos{Raw: 0},
+ },
+ },
+ },
+ }
+
+ err := attachGroupID(txgroup)
+ require.NoError(t, err)
+
+ // add signature to txn 1
+ signatureSecrets := accounts[0].sk
+ txgroup[0] = txgroup[0].Txn.Sign(signatureSecrets)
+
+ _, _, err = s.Simulate(txgroup)
+ require.NoError(t, err)
+}
diff --git a/ledger/store/accountsV2.go b/ledger/store/accountsV2.go
index 649e4111b..cb2c1d34e 100644
--- a/ledger/store/accountsV2.go
+++ b/ledger/store/accountsV2.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/accountsV2_test.go b/ledger/store/accountsV2_test.go
index a9a5bf986..c06d98200 100644
--- a/ledger/store/accountsV2_test.go
+++ b/ledger/store/accountsV2_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/blockdb/blockdb.go b/ledger/store/blockdb/blockdb.go
index 6417d8452..64b6f02fc 100644
--- a/ledger/store/blockdb/blockdb.go
+++ b/ledger/store/blockdb/blockdb.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/blockdb/blockdb_test.go b/ledger/store/blockdb/blockdb_test.go
index f659888c2..858e561e4 100644
--- a/ledger/store/blockdb/blockdb_test.go
+++ b/ledger/store/blockdb/blockdb_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/catchpoint.go b/ledger/store/catchpoint.go
index 0b4f2da66..7b8915de0 100644
--- a/ledger/store/catchpoint.go
+++ b/ledger/store/catchpoint.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/catchpointPendingHashesIter.go b/ledger/store/catchpointPendingHashesIter.go
new file mode 100644
index 000000000..69b616789
--- /dev/null
+++ b/ledger/store/catchpointPendingHashesIter.go
@@ -0,0 +1,81 @@
+// 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 store
+
+import (
+ "context"
+ "database/sql"
+)
+
+// catchpointPendingHashesIterator allows us to iterate over the hashes in the catchpointpendinghashes table in their order.
+type catchpointPendingHashesIterator struct {
+ hashCount int
+ tx *sql.Tx
+ rows *sql.Rows
+}
+
+// MakeCatchpointPendingHashesIterator create a pending hashes iterator that retrieves the hashes in the catchpointpendinghashes table.
+func MakeCatchpointPendingHashesIterator(hashCount int, tx *sql.Tx) *catchpointPendingHashesIterator {
+ return &catchpointPendingHashesIterator{
+ hashCount: hashCount,
+ tx: tx,
+ }
+}
+
+// Next returns an array containing the hashes, returning HashCount hashes at a time.
+func (iterator *catchpointPendingHashesIterator) Next(ctx context.Context) (hashes [][]byte, err error) {
+ if iterator.rows == nil {
+ iterator.rows, err = iterator.tx.QueryContext(ctx, "SELECT data FROM catchpointpendinghashes ORDER BY data")
+ if err != nil {
+ return
+ }
+ }
+
+ // gather up to accountCount encoded accounts.
+ hashes = make([][]byte, iterator.hashCount)
+ hashIdx := 0
+ for iterator.rows.Next() {
+ err = iterator.rows.Scan(&hashes[hashIdx])
+ if err != nil {
+ iterator.Close()
+ return
+ }
+
+ hashIdx++
+ if hashIdx == iterator.hashCount {
+ // we're done with this iteration.
+ return
+ }
+ }
+ hashes = hashes[:hashIdx]
+ err = iterator.rows.Err()
+ if err != nil {
+ iterator.Close()
+ return
+ }
+ // we just finished reading the table.
+ iterator.Close()
+ return
+}
+
+// Close shuts down the catchpointPendingHashesIterator, releasing database resources.
+func (iterator *catchpointPendingHashesIterator) Close() {
+ if iterator.rows != nil {
+ iterator.rows.Close()
+ iterator.rows = nil
+ }
+}
diff --git a/ledger/store/catchpoint_test.go b/ledger/store/catchpoint_test.go
index d4da07ae1..ad69ecbaf 100644
--- a/ledger/store/catchpoint_test.go
+++ b/ledger/store/catchpoint_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/data.go b/ledger/store/data.go
index d09771bd3..58edc7d45 100644
--- a/ledger/store/data.go
+++ b/ledger/store/data.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/data_test.go b/ledger/store/data_test.go
index 8e360b4bd..b6afe29a0 100644
--- a/ledger/store/data_test.go
+++ b/ledger/store/data_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/encodedAccountsIter.go b/ledger/store/encodedAccountsIter.go
new file mode 100644
index 000000000..1dd531048
--- /dev/null
+++ b/ledger/store/encodedAccountsIter.go
@@ -0,0 +1,165 @@
+// 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 store
+
+import (
+ "context"
+ "database/sql"
+
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/ledger/encoded"
+ "github.com/algorand/msgp/msgp"
+)
+
+// encodedAccountsBatchIter allows us to iterate over the accounts data stored in the accountbase table.
+type encodedAccountsBatchIter struct {
+ accountsRows *sql.Rows
+ resourcesRows *sql.Rows
+ nextBaseRow pendingBaseRow
+ nextResourceRow pendingResourceRow
+ acctResCnt catchpointAccountResourceCounter
+}
+
+// catchpointAccountResourceCounter keeps track of the resources processed for the current account
+type catchpointAccountResourceCounter struct {
+ totalAppParams uint64
+ totalAppLocalStates uint64
+ totalAssetParams uint64
+ totalAssets uint64
+}
+
+// MakeEncodedAccoutsBatchIter creates an empty accounts batch iterator.
+func MakeEncodedAccoutsBatchIter() *encodedAccountsBatchIter {
+ return &encodedAccountsBatchIter{}
+}
+
+// Next returns an array containing the account data, in the same way it appear in the database
+// returning accountCount accounts data at a time.
+func (iterator *encodedAccountsBatchIter) Next(ctx context.Context, tx *sql.Tx, accountCount int, resourceCount int) (bals []encoded.BalanceRecordV6, numAccountsProcessed uint64, err error) {
+ if iterator.accountsRows == nil {
+ iterator.accountsRows, err = tx.QueryContext(ctx, "SELECT rowid, address, data FROM accountbase ORDER BY rowid")
+ if err != nil {
+ return
+ }
+ }
+ if iterator.resourcesRows == nil {
+ iterator.resourcesRows, err = tx.QueryContext(ctx, "SELECT addrid, aidx, data FROM resources ORDER BY addrid, aidx")
+ if err != nil {
+ return
+ }
+ }
+
+ // gather up to accountCount encoded accounts.
+ bals = make([]encoded.BalanceRecordV6, 0, accountCount)
+ var encodedRecord encoded.BalanceRecordV6
+ var baseAcct BaseAccountData
+ var numAcct int
+ baseCb := func(addr basics.Address, rowid int64, accountData *BaseAccountData, encodedAccountData []byte) (err error) {
+ encodedRecord = encoded.BalanceRecordV6{Address: addr, AccountData: encodedAccountData}
+ baseAcct = *accountData
+ numAcct++
+ return nil
+ }
+
+ var totalResources int
+
+ // emptyCount := 0
+ resCb := func(addr basics.Address, cidx basics.CreatableIndex, resData *ResourcesData, encodedResourceData []byte, lastResource bool) error {
+
+ emptyBaseAcct := baseAcct.TotalAppParams == 0 && baseAcct.TotalAppLocalStates == 0 && baseAcct.TotalAssetParams == 0 && baseAcct.TotalAssets == 0
+ if !emptyBaseAcct && resData != nil {
+ if encodedRecord.Resources == nil {
+ encodedRecord.Resources = make(map[uint64]msgp.Raw)
+ }
+ encodedRecord.Resources[uint64(cidx)] = encodedResourceData
+ if resData.IsApp() && resData.IsOwning() {
+ iterator.acctResCnt.totalAppParams++
+ }
+ if resData.IsApp() && resData.IsHolding() {
+ iterator.acctResCnt.totalAppLocalStates++
+ }
+
+ if resData.IsAsset() && resData.IsOwning() {
+ iterator.acctResCnt.totalAssetParams++
+ }
+ if resData.IsAsset() && resData.IsHolding() {
+ iterator.acctResCnt.totalAssets++
+ }
+ totalResources++
+ }
+
+ if baseAcct.TotalAppParams == iterator.acctResCnt.totalAppParams &&
+ baseAcct.TotalAppLocalStates == iterator.acctResCnt.totalAppLocalStates &&
+ baseAcct.TotalAssetParams == iterator.acctResCnt.totalAssetParams &&
+ baseAcct.TotalAssets == iterator.acctResCnt.totalAssets {
+
+ encodedRecord.ExpectingMoreEntries = false
+ bals = append(bals, encodedRecord)
+ numAccountsProcessed++
+
+ iterator.acctResCnt = catchpointAccountResourceCounter{}
+
+ return nil
+ }
+
+ // max resources per chunk reached, stop iterating.
+ if lastResource {
+ encodedRecord.ExpectingMoreEntries = true
+ bals = append(bals, encodedRecord)
+ encodedRecord.Resources = nil
+ }
+
+ return nil
+ }
+
+ _, iterator.nextBaseRow, iterator.nextResourceRow, err = processAllBaseAccountRecords(
+ iterator.accountsRows, iterator.resourcesRows,
+ baseCb, resCb,
+ iterator.nextBaseRow, iterator.nextResourceRow, accountCount, resourceCount,
+ )
+ if err != nil {
+ iterator.Close()
+ return
+ }
+
+ if len(bals) == accountCount || totalResources == resourceCount {
+ // we're done with this iteration.
+ return
+ }
+
+ err = iterator.accountsRows.Err()
+ if err != nil {
+ iterator.Close()
+ return
+ }
+ // Do not Close() the iterator here. It is the caller's responsibility to
+ // do so, signalled by the return of an empty chunk. If we Close() here, the
+ // next call to Next() will start all over!
+ return
+}
+
+// Close shuts down the encodedAccountsBatchIter, releasing database resources.
+func (iterator *encodedAccountsBatchIter) Close() {
+ if iterator.accountsRows != nil {
+ iterator.accountsRows.Close()
+ iterator.accountsRows = nil
+ }
+ if iterator.resourcesRows != nil {
+ iterator.resourcesRows.Close()
+ iterator.resourcesRows = nil
+ }
+}
diff --git a/ledger/store/hashing.go b/ledger/store/hashing.go
index 40a5b6c31..fa409a2c6 100644
--- a/ledger/store/hashing.go
+++ b/ledger/store/hashing.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/interface.go b/ledger/store/interface.go
index c9f06ee53..d2f8fc4e0 100644
--- a/ledger/store/interface.go
+++ b/ledger/store/interface.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/kvsIter.go b/ledger/store/kvsIter.go
new file mode 100644
index 000000000..f831fa203
--- /dev/null
+++ b/ledger/store/kvsIter.go
@@ -0,0 +1,53 @@
+// 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 store
+
+import (
+ "context"
+ "database/sql"
+)
+
+type kvsIter struct {
+ tx *sql.Tx
+ rows *sql.Rows
+}
+
+// MakeKVsIter creates a KV iterator.
+func MakeKVsIter(ctx context.Context, tx *sql.Tx) (*kvsIter, error) {
+ rows, err := tx.QueryContext(ctx, "SELECT key, value FROM kvstore")
+ if err != nil {
+ return nil, err
+ }
+
+ return &kvsIter{
+ tx: tx,
+ rows: rows,
+ }, nil
+}
+
+func (iter *kvsIter) Next() bool {
+ return iter.rows.Next()
+}
+
+func (iter *kvsIter) KeyValue() (k []byte, v []byte, err error) {
+ err = iter.rows.Scan(&k, &v)
+ return k, v, err
+}
+
+func (iter *kvsIter) Close() {
+ iter.rows.Close()
+}
diff --git a/ledger/store/merkle_commiter.go b/ledger/store/merkle_commiter.go
index 3d837c495..bc7502dac 100644
--- a/ledger/store/merkle_commiter.go
+++ b/ledger/store/merkle_commiter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/orderedAccountsIter.go b/ledger/store/orderedAccountsIter.go
new file mode 100644
index 000000000..017de0caf
--- /dev/null
+++ b/ledger/store/orderedAccountsIter.go
@@ -0,0 +1,433 @@
+// 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 store
+
+import (
+ "context"
+ "database/sql"
+ "errors"
+ "fmt"
+ "math"
+
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/protocol"
+)
+
+// orderedAccountsIter allows us to iterate over the accounts addresses in the order of the account hashes.
+type orderedAccountsIter struct {
+ step orderedAccountsIterStep
+ accountBaseRows *sql.Rows
+ hashesRows *sql.Rows
+ resourcesRows *sql.Rows
+ tx *sql.Tx
+ pendingBaseRow pendingBaseRow
+ pendingResourceRow pendingResourceRow
+ accountCount int
+ insertStmt *sql.Stmt
+}
+
+// orderedAccountsIterStep is used by orderedAccountsIter to define the current step
+//
+//msgp:ignore orderedAccountsIterStep
+type orderedAccountsIterStep int
+
+const (
+ // startup step
+ oaiStepStartup = orderedAccountsIterStep(0)
+ // delete old ordering table if we have any leftover from previous invocation
+ oaiStepDeleteOldOrderingTable = orderedAccountsIterStep(0)
+ // create new ordering table
+ oaiStepCreateOrderingTable = orderedAccountsIterStep(1)
+ // query the existing accounts
+ oaiStepQueryAccounts = orderedAccountsIterStep(2)
+ // iterate over the existing accounts and insert their hash & address into the staging ordering table
+ oaiStepInsertAccountData = orderedAccountsIterStep(3)
+ // create an index on the ordering table so that we can efficiently scan it.
+ oaiStepCreateOrderingAccountIndex = orderedAccountsIterStep(4)
+ // query the ordering table
+ oaiStepSelectFromOrderedTable = orderedAccountsIterStep(5)
+ // iterate over the ordering table
+ oaiStepIterateOverOrderedTable = orderedAccountsIterStep(6)
+ // cleanup and delete ordering table
+ oaiStepShutdown = orderedAccountsIterStep(7)
+ // do nothing as we're done.
+ oaiStepDone = orderedAccountsIterStep(8)
+)
+
+type pendingBaseRow struct {
+ addr basics.Address
+ rowid int64
+ accountData *BaseAccountData
+ encodedAccountData []byte
+}
+
+type pendingResourceRow struct {
+ addrid int64
+ aidx basics.CreatableIndex
+ buf []byte
+}
+
+// MakeOrderedAccountsIter creates an ordered account iterator. Note that due to implementation reasons,
+// only a single iterator can be active at a time.
+func MakeOrderedAccountsIter(tx *sql.Tx, accountCount int) *orderedAccountsIter {
+ return &orderedAccountsIter{
+ tx: tx,
+ accountCount: accountCount,
+ step: oaiStepStartup,
+ }
+}
+
+// accountAddressHash is used by Next to return a single account address and the associated hash.
+type accountAddressHash struct {
+ Addrid int64
+ Digest []byte
+}
+
+// Next returns an array containing the account address and hash
+// the Next function works in multiple processing stages, where it first processes the current accounts and order them
+// followed by returning the ordered accounts. In the first phase, it would return empty accountAddressHash array
+// and sets the processedRecords to the number of accounts that were processed. On the second phase, the acct
+// would contain valid data ( and optionally the account data as well, if was asked in makeOrderedAccountsIter) and
+// the processedRecords would be zero. If err is sql.ErrNoRows it means that the iterator have completed it's work and no further
+// accounts exists. Otherwise, the caller is expected to keep calling "Next" to retrieve the next set of accounts
+// ( or let the Next function make some progress toward that goal )
+func (iterator *orderedAccountsIter) Next(ctx context.Context) (acct []accountAddressHash, processedRecords int, err error) {
+ if iterator.step == oaiStepDeleteOldOrderingTable {
+ // although we're going to delete this table anyway when completing the iterator execution, we'll try to
+ // clean up any intermediate table.
+ _, err = iterator.tx.ExecContext(ctx, "DROP TABLE IF EXISTS accountsiteratorhashes")
+ if err != nil {
+ return
+ }
+ iterator.step = oaiStepCreateOrderingTable
+ return
+ }
+ if iterator.step == oaiStepCreateOrderingTable {
+ // create the temporary table
+ _, err = iterator.tx.ExecContext(ctx, "CREATE TABLE accountsiteratorhashes(addrid INTEGER, hash blob)")
+ if err != nil {
+ return
+ }
+ iterator.step = oaiStepQueryAccounts
+ return
+ }
+ if iterator.step == oaiStepQueryAccounts {
+ // iterate over the existing accounts
+ iterator.accountBaseRows, err = iterator.tx.QueryContext(ctx, "SELECT rowid, address, data FROM accountbase ORDER BY rowid")
+ if err != nil {
+ return
+ }
+ // iterate over the existing resources
+ iterator.resourcesRows, err = iterator.tx.QueryContext(ctx, "SELECT addrid, aidx, data FROM resources ORDER BY addrid, aidx")
+ if err != nil {
+ return
+ }
+ // prepare the insert statement into the temporary table
+ iterator.insertStmt, err = iterator.tx.PrepareContext(ctx, "INSERT INTO accountsiteratorhashes(addrid, hash) VALUES(?, ?)")
+ if err != nil {
+ return
+ }
+ iterator.step = oaiStepInsertAccountData
+ return
+ }
+ if iterator.step == oaiStepInsertAccountData {
+ var lastAddrID int64
+ baseCb := func(addr basics.Address, rowid int64, accountData *BaseAccountData, encodedAccountData []byte) (err error) {
+ hash := AccountHashBuilderV6(addr, accountData, encodedAccountData)
+ _, err = iterator.insertStmt.ExecContext(ctx, rowid, hash)
+ if err != nil {
+ return
+ }
+ lastAddrID = rowid
+ return nil
+ }
+
+ resCb := func(addr basics.Address, cidx basics.CreatableIndex, resData *ResourcesData, encodedResourceData []byte, lastResource bool) error {
+ if resData != nil {
+ hash, err := ResourcesHashBuilderV6(resData, addr, cidx, resData.UpdateRound, encodedResourceData)
+ if err != nil {
+ return err
+ }
+ _, err = iterator.insertStmt.ExecContext(ctx, lastAddrID, hash)
+ return err
+ }
+ return nil
+ }
+
+ count := 0
+ count, iterator.pendingBaseRow, iterator.pendingResourceRow, err = processAllBaseAccountRecords(
+ iterator.accountBaseRows, iterator.resourcesRows,
+ baseCb, resCb,
+ iterator.pendingBaseRow, iterator.pendingResourceRow, iterator.accountCount, math.MaxInt,
+ )
+ if err != nil {
+ iterator.Close(ctx)
+ return
+ }
+
+ if count == iterator.accountCount {
+ // we're done with this iteration.
+ processedRecords = count
+ return
+ }
+
+ // make sure the resource iterator has no more entries.
+ if iterator.resourcesRows.Next() {
+ iterator.Close(ctx)
+ err = errors.New("resource table entries exceed the ones specified in the accountbase table")
+ return
+ }
+
+ processedRecords = count
+ iterator.accountBaseRows.Close()
+ iterator.accountBaseRows = nil
+ iterator.resourcesRows.Close()
+ iterator.resourcesRows = nil
+ iterator.insertStmt.Close()
+ iterator.insertStmt = nil
+ iterator.step = oaiStepCreateOrderingAccountIndex
+ return
+ }
+ if iterator.step == oaiStepCreateOrderingAccountIndex {
+ // create an index. It shown that even when we're making a single select statement in step 5, it would be better to have this index vs. not having it at all.
+ // note that this index is using the rowid of the accountsiteratorhashes table.
+ _, err = iterator.tx.ExecContext(ctx, "CREATE INDEX accountsiteratorhashesidx ON accountsiteratorhashes(hash)")
+ if err != nil {
+ iterator.Close(ctx)
+ return
+ }
+ iterator.step = oaiStepSelectFromOrderedTable
+ return
+ }
+ if iterator.step == oaiStepSelectFromOrderedTable {
+ // select the data from the ordered table
+ iterator.hashesRows, err = iterator.tx.QueryContext(ctx, "SELECT addrid, hash FROM accountsiteratorhashes ORDER BY hash")
+
+ if err != nil {
+ iterator.Close(ctx)
+ return
+ }
+ iterator.step = oaiStepIterateOverOrderedTable
+ return
+ }
+
+ if iterator.step == oaiStepIterateOverOrderedTable {
+ acct = make([]accountAddressHash, iterator.accountCount)
+ acctIdx := 0
+ for iterator.hashesRows.Next() {
+ err = iterator.hashesRows.Scan(&(acct[acctIdx].Addrid), &(acct[acctIdx].Digest))
+ if err != nil {
+ iterator.Close(ctx)
+ return
+ }
+ acctIdx++
+ if acctIdx == iterator.accountCount {
+ // we're done with this iteration.
+ return
+ }
+ }
+ acct = acct[:acctIdx]
+ iterator.step = oaiStepShutdown
+ iterator.hashesRows.Close()
+ iterator.hashesRows = nil
+ return
+ }
+ if iterator.step == oaiStepShutdown {
+ err = iterator.Close(ctx)
+ if err != nil {
+ return
+ }
+ iterator.step = oaiStepDone
+ // fallthrough
+ }
+ return nil, 0, sql.ErrNoRows
+}
+
+// Close shuts down the orderedAccountsBuilderIter, releasing database resources.
+func (iterator *orderedAccountsIter) Close(ctx context.Context) (err error) {
+ if iterator.accountBaseRows != nil {
+ iterator.accountBaseRows.Close()
+ iterator.accountBaseRows = nil
+ }
+ if iterator.resourcesRows != nil {
+ iterator.resourcesRows.Close()
+ iterator.resourcesRows = nil
+ }
+ if iterator.hashesRows != nil {
+ iterator.hashesRows.Close()
+ iterator.hashesRows = nil
+ }
+ if iterator.insertStmt != nil {
+ iterator.insertStmt.Close()
+ iterator.insertStmt = nil
+ }
+ _, err = iterator.tx.ExecContext(ctx, "DROP TABLE IF EXISTS accountsiteratorhashes")
+ return
+}
+
+func processAllBaseAccountRecords(
+ baseRows *sql.Rows,
+ resRows *sql.Rows,
+ baseCb func(addr basics.Address, rowid int64, accountData *BaseAccountData, encodedAccountData []byte) error,
+ resCb func(addr basics.Address, creatableIdx basics.CreatableIndex, resData *ResourcesData, encodedResourceData []byte, lastResource bool) error,
+ pendingBase pendingBaseRow, pendingResource pendingResourceRow, accountCount int, resourceCount int,
+) (int, pendingBaseRow, pendingResourceRow, error) {
+ var addr basics.Address
+ var prevAddr basics.Address
+ var err error
+ count := 0
+
+ var accountData BaseAccountData
+ var addrbuf []byte
+ var buf []byte
+ var rowid int64
+ for {
+ if pendingBase.rowid != 0 {
+ addr = pendingBase.addr
+ rowid = pendingBase.rowid
+ accountData = *pendingBase.accountData
+ buf = pendingBase.encodedAccountData
+ pendingBase = pendingBaseRow{}
+ } else {
+ if !baseRows.Next() {
+ break
+ }
+
+ err = baseRows.Scan(&rowid, &addrbuf, &buf)
+ if err != nil {
+ return 0, pendingBaseRow{}, pendingResourceRow{}, err
+ }
+
+ if len(addrbuf) != len(addr) {
+ err = fmt.Errorf("account DB address length mismatch: %d != %d", len(addrbuf), len(addr))
+ return 0, pendingBaseRow{}, pendingResourceRow{}, err
+ }
+
+ copy(addr[:], addrbuf)
+
+ accountData = BaseAccountData{}
+ err = protocol.Decode(buf, &accountData)
+ if err != nil {
+ return 0, pendingBaseRow{}, pendingResourceRow{}, err
+ }
+ }
+
+ err = baseCb(addr, rowid, &accountData, buf)
+ if err != nil {
+ return 0, pendingBaseRow{}, pendingResourceRow{}, err
+ }
+
+ var resourcesProcessed int
+ pendingResource, resourcesProcessed, err = processAllResources(resRows, addr, &accountData, rowid, pendingResource, resourceCount, resCb)
+ if err != nil {
+ err = fmt.Errorf("failed to gather resources for account %v, addrid %d, prev address %v : %w", addr, rowid, prevAddr, err)
+ return 0, pendingBaseRow{}, pendingResourceRow{}, err
+ }
+
+ if resourcesProcessed == resourceCount {
+ // we're done with this iteration.
+ pendingBase := pendingBaseRow{
+ addr: addr,
+ rowid: rowid,
+ accountData: &accountData,
+ encodedAccountData: buf,
+ }
+ return count, pendingBase, pendingResource, nil
+ }
+ resourceCount -= resourcesProcessed
+
+ count++
+ if accountCount > 0 && count == accountCount {
+ // we're done with this iteration.
+ return count, pendingBaseRow{}, pendingResource, nil
+ }
+ prevAddr = addr
+ }
+
+ return count, pendingBaseRow{}, pendingResource, nil
+}
+
+func processAllResources(
+ resRows *sql.Rows,
+ addr basics.Address, accountData *BaseAccountData, acctRowid int64, pr pendingResourceRow, resourceCount int,
+ callback func(addr basics.Address, creatableIdx basics.CreatableIndex, resData *ResourcesData, encodedResourceData []byte, lastResource bool) error,
+) (pendingResourceRow, int, error) {
+ var err error
+ count := 0
+
+ // Declare variabled outside of the loop to prevent allocations per iteration.
+ // At least resData is resolved as "escaped" because of passing it by a pointer to protocol.Decode()
+ var buf []byte
+ var addrid int64
+ var aidx basics.CreatableIndex
+ var resData ResourcesData
+ for {
+ if pr.addrid != 0 {
+ // some accounts may not have resources, consider the following case:
+ // acct 1 and 3 has resources, account 2 does not
+ // in this case addrid = 3 after processing resources from 1, but acctRowid = 2
+ // and we need to skip accounts without resources
+ if pr.addrid > acctRowid {
+ err = callback(addr, 0, nil, nil, false)
+ return pr, count, err
+ }
+ if pr.addrid < acctRowid {
+ err = fmt.Errorf("resource table entries mismatches accountbase table entries : reached addrid %d while expecting resource for %d", pr.addrid, acctRowid)
+ return pendingResourceRow{}, count, err
+ }
+ addrid = pr.addrid
+ buf = pr.buf
+ aidx = pr.aidx
+ pr = pendingResourceRow{}
+ } else {
+ if !resRows.Next() {
+ err = callback(addr, 0, nil, nil, false)
+ if err != nil {
+ return pendingResourceRow{}, count, err
+ }
+ break
+ }
+ err = resRows.Scan(&addrid, &aidx, &buf)
+ if err != nil {
+ return pendingResourceRow{}, count, err
+ }
+ if addrid < acctRowid {
+ err = fmt.Errorf("resource table entries mismatches accountbase table entries : reached addrid %d while expecting resource for %d", addrid, acctRowid)
+ return pendingResourceRow{}, count, err
+ } else if addrid > acctRowid {
+ err = callback(addr, 0, nil, nil, false)
+ return pendingResourceRow{addrid, aidx, buf}, count, err
+ }
+ }
+ resData = ResourcesData{}
+ err = protocol.Decode(buf, &resData)
+ if err != nil {
+ return pendingResourceRow{}, count, err
+ }
+ count++
+ if resourceCount > 0 && count == resourceCount {
+ // last resource to be included in chunk
+ err := callback(addr, aidx, &resData, buf, true)
+ return pendingResourceRow{}, count, err
+ }
+ err = callback(addr, aidx, &resData, buf, false)
+ if err != nil {
+ return pendingResourceRow{}, count, err
+ }
+ }
+ return pendingResourceRow{}, count, nil
+}
diff --git a/ledger/store/schema.go b/ledger/store/schema.go
index 84bab3f1c..acbd102b1 100644
--- a/ledger/store/schema.go
+++ b/ledger/store/schema.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/schema_test.go b/ledger/store/schema_test.go
index 6074a3f5e..cab8c9b71 100644
--- a/ledger/store/schema_test.go
+++ b/ledger/store/schema_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/sql.go b/ledger/store/sql.go
index c7f8bb309..42c1ebf8f 100644
--- a/ledger/store/sql.go
+++ b/ledger/store/sql.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/sql_test.go b/ledger/store/sql_test.go
index 2eb96bfe6..3bacf343a 100644
--- a/ledger/store/sql_test.go
+++ b/ledger/store/sql_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/testing.go b/ledger/store/testing.go
index c637076c1..0e426a28c 100644
--- a/ledger/store/testing.go
+++ b/ledger/store/testing.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/testing/helpers.go b/ledger/store/testing/helpers.go
index 0fb5ec824..34ba3e3ff 100644
--- a/ledger/store/testing/helpers.go
+++ b/ledger/store/testing/helpers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/store/trackerdbV2.go b/ledger/store/trackerdbV2.go
index feb7604ca..19a8fbcaa 100644
--- a/ledger/store/trackerdbV2.go
+++ b/ledger/store/trackerdbV2.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/testing/accountsTotals.go b/ledger/testing/accountsTotals.go
index bad08294e..48109cc84 100644
--- a/ledger/testing/accountsTotals.go
+++ b/ledger/testing/accountsTotals.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/testing/consensusRange.go b/ledger/testing/consensusRange.go
index 877e03fae..e96bcc728 100644
--- a/ledger/testing/consensusRange.go
+++ b/ledger/testing/consensusRange.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/testing/consensusRange_test.go b/ledger/testing/consensusRange_test.go
index df51ec720..325373a39 100644
--- a/ledger/testing/consensusRange_test.go
+++ b/ledger/testing/consensusRange_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/testing/initState.go b/ledger/testing/initState.go
index 559d03b8a..00cbe0d1b 100644
--- a/ledger/testing/initState.go
+++ b/ledger/testing/initState.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -24,6 +24,7 @@ import (
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
+ basics_testing "github.com/algorand/go-algorand/data/basics/testing"
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/protocol"
@@ -65,15 +66,15 @@ func GenerateInitState(tb testing.TB, proto protocol.ConsensusVersion, baseAlgoP
for i := range genaddrs {
initKeys[genaddrs[i]] = gensecrets[i]
// Give each account quite a bit more balance than MinFee or MinBalance
- ad := basics.MakeAccountData(basics.Online, basics.MicroAlgos{Raw: uint64((i + baseAlgoPerAccount) * 100000)})
+ ad := basics_testing.MakeAccountData(basics.Online, basics.MicroAlgos{Raw: uint64((i + baseAlgoPerAccount) * 100000)})
ad.VoteFirstValid = 1
ad.VoteLastValid = 100_000
initAccounts[genaddrs[i]] = ad
}
initKeys[poolAddr] = poolSecret
- initAccounts[poolAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 1234567})
+ initAccounts[poolAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 1234567})
initKeys[sinkAddr] = sinkSecret
- initAccounts[sinkAddr] = basics.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 7654321})
+ initAccounts[sinkAddr] = basics_testing.MakeAccountData(basics.NotParticipating, basics.MicroAlgos{Raw: 7654321})
incentivePoolBalanceAtGenesis := initAccounts[poolAddr].MicroAlgos
var initialRewardsPerRound uint64
diff --git a/ledger/testing/randomAccounts.go b/ledger/testing/randomAccounts.go
index c3c559911..9fb995349 100644
--- a/ledger/testing/randomAccounts.go
+++ b/ledger/testing/randomAccounts.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -34,6 +34,16 @@ import (
var testPoolAddr = basics.Address{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
var testSinkAddr = basics.Address{0x2c, 0x2a, 0x6c, 0xe9, 0xa9, 0xa7, 0xc2, 0x8c, 0x22, 0x95, 0xfd, 0x32, 0x4f, 0x77, 0xa5, 0x4, 0x8b, 0x42, 0xc2, 0xb7, 0xa8, 0x54, 0x84, 0xb6, 0x80, 0xb1, 0xe1, 0x3d, 0x59, 0x9b, 0xeb, 0x36}
+// PoolAddr returns a copy of the test pool address
+func PoolAddr() basics.Address {
+ return testPoolAddr
+}
+
+// SinkAddr returns a copy of the test sink address
+func SinkAddr() basics.Address {
+ return testSinkAddr
+}
+
// RandomAddress generates a random address
func RandomAddress() basics.Address {
var addr basics.Address
diff --git a/ledger/testing/randomAccounts_test.go b/ledger/testing/randomAccounts_test.go
index 97744f497..0829c4340 100644
--- a/ledger/testing/randomAccounts_test.go
+++ b/ledger/testing/randomAccounts_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/testing/testGenesis.go b/ledger/testing/testGenesis.go
index 609118f24..80d752c75 100644
--- a/ledger/testing/testGenesis.go
+++ b/ledger/testing/testGenesis.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/tracker.go b/ledger/tracker.go
index 161285644..b87c6fbe3 100644
--- a/ledger/tracker.go
+++ b/ledger/tracker.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/tracker_test.go b/ledger/tracker_test.go
index 0f319ff55..8be223d6f 100644
--- a/ledger/tracker_test.go
+++ b/ledger/tracker_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/trackerdb.go b/ledger/trackerdb.go
index 8a6dda4bf..17c6872d9 100644
--- a/ledger/trackerdb.go
+++ b/ledger/trackerdb.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/txnbench_test.go b/ledger/txnbench_test.go
index 788ecbe1f..ddc7aeba9 100644
--- a/ledger/txnbench_test.go
+++ b/ledger/txnbench_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/txtail.go b/ledger/txtail.go
index b262b3ce3..879f0ee7d 100644
--- a/ledger/txtail.go
+++ b/ledger/txtail.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/txtail_test.go b/ledger/txtail_test.go
index 1f0d06a29..fce531aa9 100644
--- a/ledger/txtail_test.go
+++ b/ledger/txtail_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/ledger/voters.go b/ledger/voters.go
index d0a76a6cd..1648e707f 100644
--- a/ledger/voters.go
+++ b/ledger/voters.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -18,9 +18,10 @@ package ledger
import (
"fmt"
- "github.com/algorand/go-algorand/stateproof"
"sync"
+ "github.com/algorand/go-algorand/stateproof"
+
"github.com/algorand/go-algorand/config"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/bookkeeping"
diff --git a/ledger/voters_test.go b/ledger/voters_test.go
index 78e9eb7b8..892d3679c 100644
--- a/ledger/voters_test.go
+++ b/ledger/voters_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/accounts.go b/libgoal/accounts.go
index 3fc02fc88..f9e223e69 100644
--- a/libgoal/accounts.go
+++ b/libgoal/accounts.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/error.go b/libgoal/error.go
index 63ce176aa..ca1801692 100644
--- a/libgoal/error.go
+++ b/libgoal/error.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/libgoal.go b/libgoal/libgoal.go
index c76489079..4a27b7058 100644
--- a/libgoal/libgoal.go
+++ b/libgoal/libgoal.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -765,7 +765,7 @@ func (c *Client) ApplicationBoxes(appID uint64, maxBoxNum uint64) (resp model.Bo
}
// GetApplicationBoxByName takes an app's index and box name and returns its value.
-// The box name should be of the form `encoding:value`. See logic.AppCallBytes for more information.
+// The box name should be of the form `encoding:value`. See apps.AppCallBytes for more information.
func (c *Client) GetApplicationBoxByName(index uint64, name string) (resp model.BoxResponse, err error) {
algod, err := c.ensureAlgodClient()
if err == nil {
diff --git a/libgoal/libgoal_test.go b/libgoal/libgoal_test.go
index 96c524ef4..87df50ad9 100644
--- a/libgoal/libgoal_test.go
+++ b/libgoal/libgoal_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/lockedFile.go b/libgoal/lockedFile.go
index 26b235d24..6b09db8e3 100644
--- a/libgoal/lockedFile.go
+++ b/libgoal/lockedFile.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/lockedFileLinux.go b/libgoal/lockedFileLinux.go
index 9c60ca028..eb90377f1 100644
--- a/libgoal/lockedFileLinux.go
+++ b/libgoal/lockedFileLinux.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/lockedFileUnix.go b/libgoal/lockedFileUnix.go
index b32af5def..0d2b50e62 100644
--- a/libgoal/lockedFileUnix.go
+++ b/libgoal/lockedFileUnix.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/lockedFileWindows.go b/libgoal/lockedFileWindows.go
index c89785d80..337943a5c 100644
--- a/libgoal/lockedFileWindows.go
+++ b/libgoal/lockedFileWindows.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/participation.go b/libgoal/participation.go
index 7bdb8981b..f57629a36 100644
--- a/libgoal/participation.go
+++ b/libgoal/participation.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/system.go b/libgoal/system.go
index 6cf716671..a72214dfb 100644
--- a/libgoal/system.go
+++ b/libgoal/system.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/teal.go b/libgoal/teal.go
index f0b18ede6..90295799c 100644
--- a/libgoal/teal.go
+++ b/libgoal/teal.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/transactions.go b/libgoal/transactions.go
index ac2eaf26a..5ff9e3bba 100644
--- a/libgoal/transactions.go
+++ b/libgoal/transactions.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/unencryptedWallet.go b/libgoal/unencryptedWallet.go
index 8953fe0ea..35b74d754 100644
--- a/libgoal/unencryptedWallet.go
+++ b/libgoal/unencryptedWallet.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/walletHandles.go b/libgoal/walletHandles.go
index c587c9af5..302d46595 100644
--- a/libgoal/walletHandles.go
+++ b/libgoal/walletHandles.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/libgoal/wallets.go b/libgoal/wallets.go
index 5ba91a5a2..3e0f12d54 100644
--- a/libgoal/wallets.go
+++ b/libgoal/wallets.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/collector.go b/logging/collector.go
index 7e3bedf2d..39203964c 100644
--- a/logging/collector.go
+++ b/logging/collector.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/cyclicWriter.go b/logging/cyclicWriter.go
index 0c88955df..d6166a82a 100644
--- a/logging/cyclicWriter.go
+++ b/logging/cyclicWriter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/cyclicWriter_test.go b/logging/cyclicWriter_test.go
index 5719be930..d52b826bd 100644
--- a/logging/cyclicWriter_test.go
+++ b/logging/cyclicWriter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/log.go b/logging/log.go
index 1849774ed..ebe600f7f 100644
--- a/logging/log.go
+++ b/logging/log.go
@@ -134,6 +134,9 @@ type Logger interface {
// Set the logging version (Info by default)
SetLevel(Level)
+ // Get the logging version
+ GetLevel() Level
+
// Sets the output target
SetOutput(io.Writer)
@@ -155,7 +158,6 @@ type Logger interface {
Metrics(category telemetryspec.Category, metrics telemetryspec.MetricDetails, details interface{})
Event(category telemetryspec.Category, identifier telemetryspec.Event)
EventWithDetails(category telemetryspec.Category, identifier telemetryspec.Event, details interface{})
- StartOperation(category telemetryspec.Category, identifier telemetryspec.Operation) TelemetryOperation
GetTelemetrySession() string
GetTelemetryGUID() string
GetInstanceName() string
@@ -285,6 +287,10 @@ func (l logger) WithFields(fields Fields) Logger {
}
}
+func (l logger) GetLevel() (lvl Level) {
+ return Level(l.entry.Logger.Level)
+}
+
func (l logger) SetLevel(lvl Level) {
l.entry.Logger.Level = logrus.Level(lvl)
}
@@ -451,13 +457,6 @@ func (l logger) EventWithDetails(category telemetryspec.Category, identifier tel
}
}
-func (l logger) StartOperation(category telemetryspec.Category, identifier telemetryspec.Operation) TelemetryOperation {
- if l.loggerState.telemetry != nil {
- return l.loggerState.telemetry.logStartOperation(l, category, identifier)
- }
- return TelemetryOperation{}
-}
-
func (l logger) CloseTelemetry() {
if l.loggerState.telemetry != nil {
l.loggerState.telemetry.Close()
diff --git a/logging/logBuffer.go b/logging/logBuffer.go
index 8064e0d17..26b1045f2 100644
--- a/logging/logBuffer.go
+++ b/logging/logBuffer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/logBuffer_test.go b/logging/logBuffer_test.go
index b2f8dc1e1..04a87f3f6 100644
--- a/logging/logBuffer_test.go
+++ b/logging/logBuffer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/log_test.go b/logging/log_test.go
index 2f4365ccb..af62bae91 100644
--- a/logging/log_test.go
+++ b/logging/log_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -60,6 +60,15 @@ func TestFileOutputNewLogger(t *testing.T) {
}
+func TestSetGetLevel(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ nl := NewLogger()
+ require.Equal(t, Info, nl.GetLevel())
+ nl.SetLevel(Error)
+ require.Equal(t, Error, nl.GetLevel())
+}
+
func TestSetLevelNewLogger(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
diff --git a/logging/logspec/agreement.go b/logging/logspec/agreement.go
index 6091891fb..45fe5e1b6 100644
--- a/logging/logspec/agreement.go
+++ b/logging/logspec/agreement.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/logspec/ledger.go b/logging/logspec/ledger.go
index e7538b562..0ed363340 100644
--- a/logging/logspec/ledger.go
+++ b/logging/logspec/ledger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/logspec/root.go b/logging/logspec/root.go
index 1bec1eb96..6dd08d1d5 100644
--- a/logging/logspec/root.go
+++ b/logging/logspec/root.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/telemetry.go b/logging/telemetry.go
index 6ed62bf40..7daa47192 100644
--- a/logging/telemetry.go
+++ b/logging/telemetry.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -244,12 +244,6 @@ func (t *telemetryState) logEvent(l logger, category telemetryspec.Category, ide
t.logTelemetry(l, buildMessage(string(category), string(identifier)), details)
}
-func (t *telemetryState) logStartOperation(l logger, category telemetryspec.Category, identifier telemetryspec.Operation) TelemetryOperation {
- op := makeTelemetryOperation(t, category, identifier)
- t.logTelemetry(l, buildMessage(string(category), string(identifier), "Start"), nil)
- return op
-}
-
func buildMessage(args ...string) string {
message := telemetryPrefix + strings.Join(args, telemetrySeparator)
return message
diff --git a/logging/telemetryCommon.go b/logging/telemetryCommon.go
index d4d57a817..41a13d949 100644
--- a/logging/telemetryCommon.go
+++ b/logging/telemetryCommon.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -17,24 +17,11 @@
package logging
import (
- "sync"
- "time"
-
"github.com/algorand/go-deadlock"
"github.com/sirupsen/logrus"
-
- "github.com/algorand/go-algorand/logging/telemetryspec"
+ "sync"
)
-// TelemetryOperation wraps the context for an ongoing telemetry.StartOperation call
-type TelemetryOperation struct {
- startTime time.Time
- category telemetryspec.Category
- identifier telemetryspec.Operation
- telemetryState *telemetryState
- pending int32
-}
-
type telemetryHook interface {
Fire(entry *logrus.Entry) error
Levels() []logrus.Level
diff --git a/logging/telemetryConfig.go b/logging/telemetryConfig.go
index 452202f91..eefd84e4a 100644
--- a/logging/telemetryConfig.go
+++ b/logging/telemetryConfig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/telemetryConfig_test.go b/logging/telemetryConfig_test.go
index dd7cce322..8a61ba41c 100644
--- a/logging/telemetryConfig_test.go
+++ b/logging/telemetryConfig_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/telemetryFilteredHook.go b/logging/telemetryFilteredHook.go
index 86c402b87..c92557783 100644
--- a/logging/telemetryFilteredHook.go
+++ b/logging/telemetryFilteredHook.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/telemetryOperation.go b/logging/telemetryOperation.go
deleted file mode 100644
index 077902dd2..000000000
--- a/logging/telemetryOperation.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2019-2022 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 logging
-
-import (
- "sync/atomic"
- "time"
-
- "github.com/sirupsen/logrus"
-
- "github.com/algorand/go-algorand/logging/telemetryspec"
-)
-
-func makeTelemetryOperation(telemetryState *telemetryState, category telemetryspec.Category, identifier telemetryspec.Operation) TelemetryOperation {
- return TelemetryOperation{
- startTime: time.Now(),
- category: category,
- identifier: identifier,
- telemetryState: telemetryState,
- pending: 1, // Indicates we should process Stop() when called
- }
-}
-
-// Stop is called to report the completion of an operation started by logger.StartOperation
-func (op *TelemetryOperation) Stop(l logger, details interface{}) {
- // If we have already called Stop, or if we're a nil operation, don't do anything
- if !atomic.CompareAndSwapInt32(&op.pending, 1, 0) {
- return
- }
-
- elapsed := time.Since(op.startTime).Nanoseconds()
- entry := l.WithFields(logrus.Fields{
- "duration": elapsed,
- }).(logger)
-
- op.telemetryState.logTelemetry(entry, buildMessage(string(op.category), string(op.identifier), "Stop"), details)
-}
diff --git a/logging/telemetry_test.go b/logging/telemetry_test.go
index a8a913831..1d1b9f8d4 100644
--- a/logging/telemetry_test.go
+++ b/logging/telemetry_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -19,12 +19,10 @@ package logging
import (
"encoding/json"
"fmt"
- "os"
- "testing"
- "time"
-
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
+ "os"
+ "testing"
"github.com/algorand/go-deadlock"
@@ -159,17 +157,11 @@ func TestTelemetryHook(t *testing.T) {
f.telem.logMetrics(f.l, testString1, testMetrics{}, nil)
f.telem.logEvent(f.l, testString1, testString2, nil)
- op := f.telem.logStartOperation(f.l, testString1, testString2)
- time.Sleep(1 * time.Millisecond)
- op.Stop(f.l, nil)
entries := f.hookEntries()
- a.Equal(4, len(entries))
+ a.Equal(2, len(entries))
a.Equal(buildMessage(testString1, testString2), entries[0])
a.Equal(buildMessage(testString1, testString2), entries[1])
- a.Equal(buildMessage(testString1, testString2, "Start"), entries[2])
- a.Equal(buildMessage(testString1, testString2, "Stop"), entries[3])
- a.NotZero(f.hookData()[3]["duration"])
}
func TestNilMetrics(t *testing.T) {
@@ -182,23 +174,6 @@ func TestNilMetrics(t *testing.T) {
a.Zero(len(f.hookEntries()))
}
-func TestMultipleOperationStop(t *testing.T) {
- partitiontest.PartitionTest(t)
- a := require.New(t)
- f := makeTelemetryTestFixture(logrus.InfoLevel)
-
- op := f.telem.logStartOperation(f.l, testString1, testString2)
- op.Stop(f.l, nil)
-
- // Start and stop should result in 2 entries
- a.Equal(2, len(f.hookEntries()))
-
- op.Stop(f.l, nil)
-
- // Calling stop again should not result in another entry
- a.Equal(2, len(f.hookEntries()))
-}
-
func TestDetails(t *testing.T) {
partitiontest.PartitionTest(t)
a := require.New(t)
diff --git a/logging/telemetryhook.go b/logging/telemetryhook.go
index 88ef93233..8e036eacd 100644
--- a/logging/telemetryhook.go
+++ b/logging/telemetryhook.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -222,9 +222,9 @@ func (el elasticClientLogger) Printf(format string, v ...interface{}) {
case logrus.InfoLevel:
el.logger.Infof(format, v...)
case logrus.WarnLevel:
- el.logger.Warnf(format, v...)
+ el.logger.WithFields(Fields{"TelemetryError": true}).Warnf(format, v...)
default:
- el.logger.Errorf(format, v...)
+ el.logger.WithFields(Fields{"TelemetryError": true}).Errorf(format, v...)
}
}
diff --git a/logging/telemetryhook_test.go b/logging/telemetryhook_test.go
index 40c25832f..43bc1acf4 100644
--- a/logging/telemetryhook_test.go
+++ b/logging/telemetryhook_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/telemetryspec/category.go b/logging/telemetryspec/category.go
index c7a8acf7a..44077c722 100644
--- a/logging/telemetryspec/category.go
+++ b/logging/telemetryspec/category.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/telemetryspec/event.go b/logging/telemetryspec/event.go
index 0cc633429..34d161bce 100644
--- a/logging/telemetryspec/event.go
+++ b/logging/telemetryspec/event.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -99,17 +99,6 @@ type BlockAcceptedEventDetails struct {
VoteBufLen uint64
}
-// TopAccountsEvent event
-const TopAccountsEvent Event = "TopAccounts"
-
-// TopAccountEventDetails contains details for the BlockAcceptedEvent
-type TopAccountEventDetails struct {
- Round uint64
- OnlineAccounts []map[string]interface{}
- OnlineCirculation uint64
- OfflineCirculation uint64
-}
-
// AccountRegisteredEvent event
const AccountRegisteredEvent Event = "AccountRegistered"
diff --git a/logging/telemetryspec/eventTiming.go b/logging/telemetryspec/eventTiming.go
index a43177a89..be6f80328 100644
--- a/logging/telemetryspec/eventTiming.go
+++ b/logging/telemetryspec/eventTiming.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/telemetryspec/metric.go b/logging/telemetryspec/metric.go
index ec538769c..fcb6c1d45 100644
--- a/logging/telemetryspec/metric.go
+++ b/logging/telemetryspec/metric.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -18,6 +18,7 @@ package telemetryspec
import (
"bytes"
+ "encoding/json"
"fmt"
"strconv"
"strings"
@@ -60,7 +61,7 @@ type AssembleBlockStats struct {
TotalLength uint64
EarlyCommittedCount uint64 // number of transaction groups that were pending on the transaction pool but have been included in previous block
Nanoseconds int64
- ProcessingTime transactionProcessingTimeDistibution
+ ProcessingTime transactionProcessingTimeDistribution
BlockGenerationDuration uint64
TransactionsLoopStartTime int64
StateProofNextRound uint64 // next round for which state proof if expected
@@ -215,7 +216,7 @@ func (m AccountsUpdateMetrics) Identifier() Metric {
return accountsUpdateMetricsIdentifier
}
-type transactionProcessingTimeDistibution struct {
+type transactionProcessingTimeDistribution struct {
// 10 buckets: 0-100Kns, 100Kns-200Kns .. 900Kns-1ms
// 9 buckets: 1ms-2ms .. 9ms-10ms
// 9 buckets: 10ms-20ms .. 90ms-100ms
@@ -226,7 +227,7 @@ type transactionProcessingTimeDistibution struct {
// MarshalJSON supports json.Marshaler interface
// generate comma delimited text representing the transaction processing timing
-func (t transactionProcessingTimeDistibution) MarshalJSON() ([]byte, error) {
+func (t transactionProcessingTimeDistribution) MarshalJSON() ([]byte, error) {
var outStr strings.Builder
outStr.WriteString("[")
for i, bucket := range t.transactionBuckets {
@@ -239,7 +240,7 @@ func (t transactionProcessingTimeDistibution) MarshalJSON() ([]byte, error) {
return []byte(outStr.String()), nil
}
-func (t *transactionProcessingTimeDistibution) AddTransaction(duration time.Duration) {
+func (t *transactionProcessingTimeDistribution) AddTransaction(duration time.Duration) {
var idx int64
if duration < 10*time.Millisecond {
if duration < time.Millisecond {
@@ -260,3 +261,53 @@ func (t *transactionProcessingTimeDistibution) AddTransaction(duration time.Dura
t.transactionBuckets[idx]++
}
}
+
+func (t *transactionProcessingTimeDistribution) UnmarshalJSON(data []byte) error {
+ var arr []json.Number
+ if err := json.Unmarshal(data, &arr); err != nil {
+ return err
+ }
+ if len(arr) != len(t.transactionBuckets) {
+ return fmt.Errorf("array has %d buckets, should have %d", len(arr), len(t.transactionBuckets))
+ }
+ for i := range t.transactionBuckets {
+ val, err := arr[i].Int64()
+ if err != nil {
+ return fmt.Errorf("bucket has invalid value %s", arr[i])
+ }
+ t.transactionBuckets[i] = int(val)
+ }
+ return nil
+}
+
+func (t *transactionProcessingTimeDistribution) MarshalString() string {
+ var out strings.Builder
+ var offset int
+ var base, mul time.Duration
+bucketloop:
+ for i, val := range t.transactionBuckets {
+ switch {
+ case i < 10:
+ mul = 100000 * time.Nanosecond
+ case i < 19:
+ mul = time.Millisecond
+ base = mul
+ offset = 10
+ case i < 28:
+ mul = 10 * time.Millisecond
+ base = mul
+ offset = 19
+ case i < 37:
+ mul = 100 * time.Millisecond
+ base = mul
+ offset = 28
+ case i == 37:
+ break bucketloop
+ }
+ start := base + time.Duration(i-offset)*mul
+ end := base + time.Duration(i+1-offset)*mul
+ out.WriteString(fmt.Sprintf("%s - %s: %d\n", start, end, val))
+ }
+ out.WriteString(fmt.Sprintf(">1s: %d\n", t.transactionBuckets[37]))
+ return out.String()
+}
diff --git a/logging/telemetryspec/metric_test.go b/logging/telemetryspec/metric_test.go
index c6d6489da..85693bdba 100644
--- a/logging/telemetryspec/metric_test.go
+++ b/logging/telemetryspec/metric_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -27,9 +27,9 @@ import (
"github.com/algorand/go-algorand/test/partitiontest"
)
-func TestTransactionProcessingTimeDistibutionFormatting(t *testing.T) {
+func TestTransactionProcessingTimeDistributionFormatting(t *testing.T) {
partitiontest.PartitionTest(t)
- var processingTime transactionProcessingTimeDistibution
+ var processingTime transactionProcessingTimeDistribution
processingTime.AddTransaction(50000 * time.Nanosecond)
processingTime.AddTransaction(80000 * time.Nanosecond)
processingTime.AddTransaction(120000 * time.Nanosecond)
@@ -39,10 +39,15 @@ func TestTransactionProcessingTimeDistibutionFormatting(t *testing.T) {
processingTime.AddTransaction(2 * time.Millisecond)
bytes, err := processingTime.MarshalJSON()
require.NoError(t, err)
- require.Equal(t, []byte("[2,3,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"), bytes)
+ expected := "[2,3,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"
+ require.Equal(t, []byte(expected), bytes)
+
+ var decPT transactionProcessingTimeDistribution
+ require.NoError(t, json.Unmarshal([]byte(expected), &decPT))
+ require.Equal(t, processingTime, decPT)
container := struct {
- ProcessingTime transactionProcessingTimeDistibution
+ ProcessingTime transactionProcessingTimeDistribution
}{ProcessingTime: processingTime}
bytes, err = json.Marshal(container)
@@ -50,6 +55,15 @@ func TestTransactionProcessingTimeDistibutionFormatting(t *testing.T) {
require.Equal(t, []byte("{\"ProcessingTime\":[2,3,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}"), bytes)
}
+func TestTransactionProcessingTimeDistributionPrint(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ var decPT transactionProcessingTimeDistribution
+ expected := "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38]"
+ require.NoError(t, json.Unmarshal([]byte(expected), &decPT))
+ t.Log("\n" + decPT.MarshalString())
+}
+
func TestAssembleBlockStatsString(t *testing.T) {
partitiontest.PartitionTest(t)
diff --git a/logging/testingLogger.go b/logging/testingLogger.go
index 60696b963..6492c7ddd 100644
--- a/logging/testingLogger.go
+++ b/logging/testingLogger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/logging/usage.go b/logging/usage.go
index da668a72a..2a668ceb7 100644
--- a/logging/usage.go
+++ b/logging/usage.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -54,7 +54,7 @@ func UsageLogThread(ctx context.Context, log Logger, period time.Duration, wg *s
Utime, Stime, _ = util.GetCurrentProcessTimes()
runtime.ReadMemStats(&mst)
- ramUsageGauge.Set(float64(mst.HeapInuse))
+ ramUsageGauge.Set(uint64(mst.HeapInuse))
if hasPrev {
userNanos := Utime - prevUtime
diff --git a/netdeploy/network.go b/netdeploy/network.go
index 6d14819c4..1d5e503de 100644
--- a/netdeploy/network.go
+++ b/netdeploy/network.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/networkTemplate.go b/netdeploy/networkTemplate.go
index 545617928..b0a913a44 100644
--- a/netdeploy/networkTemplate.go
+++ b/netdeploy/networkTemplate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/networkTemplates_test.go b/netdeploy/networkTemplates_test.go
index c3e6445bd..f8e3ee685 100644
--- a/netdeploy/networkTemplates_test.go
+++ b/netdeploy/networkTemplates_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/network_test.go b/netdeploy/network_test.go
index 383d7f952..dce8a10e4 100644
--- a/netdeploy/network_test.go
+++ b/netdeploy/network_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/bootstrappedNetwork.go b/netdeploy/remote/bootstrappedNetwork.go
index 3738ba466..855a2ea3b 100644
--- a/netdeploy/remote/bootstrappedNetwork.go
+++ b/netdeploy/remote/bootstrappedNetwork.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/bootstrappedNetwork_test.go b/netdeploy/remote/bootstrappedNetwork_test.go
index 1c9f14e6e..ac0afa642 100644
--- a/netdeploy/remote/bootstrappedNetwork_test.go
+++ b/netdeploy/remote/bootstrappedNetwork_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/buildConfig.go b/netdeploy/remote/buildConfig.go
index 22e35e21e..bffb85f0a 100644
--- a/netdeploy/remote/buildConfig.go
+++ b/netdeploy/remote/buildConfig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/deployedNetwork.go b/netdeploy/remote/deployedNetwork.go
index 353389c99..a6b7be3f7 100644
--- a/netdeploy/remote/deployedNetwork.go
+++ b/netdeploy/remote/deployedNetwork.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/deployedNetwork_test.go b/netdeploy/remote/deployedNetwork_test.go
index f33e7cea2..98149ac32 100644
--- a/netdeploy/remote/deployedNetwork_test.go
+++ b/netdeploy/remote/deployedNetwork_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/hostConfig.go b/netdeploy/remote/hostConfig.go
index d9710b80b..34af36ef4 100644
--- a/netdeploy/remote/hostConfig.go
+++ b/netdeploy/remote/hostConfig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/hostTemplate.go b/netdeploy/remote/hostTemplate.go
index faeccb3d0..530c80b32 100644
--- a/netdeploy/remote/hostTemplate.go
+++ b/netdeploy/remote/hostTemplate.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/nodeConfig.go b/netdeploy/remote/nodeConfig.go
index 143271d5d..2a6eee0bf 100644
--- a/netdeploy/remote/nodeConfig.go
+++ b/netdeploy/remote/nodeConfig.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/nodeWalletData.go b/netdeploy/remote/nodeWalletData.go
index 1f08ea252..87ca8d41c 100644
--- a/netdeploy/remote/nodeWalletData.go
+++ b/netdeploy/remote/nodeWalletData.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/netdeploy/remote/nodecfg/nodeConfigurator.go b/netdeploy/remote/nodecfg/nodeConfigurator.go
index 2d286f7de..4621373b2 100644
--- a/netdeploy/remote/nodecfg/nodeConfigurator.go
+++ b/netdeploy/remote/nodecfg/nodeConfigurator.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -87,7 +87,7 @@ func (nc *nodeConfigurator) apply(rootConfigDir, rootNodeDir string) (err error)
for _, nodeDir := range nodeDirs {
nodeDir.delaySave = true
- err = nodeDir.configure(nc.dnsName)
+ err = nodeDir.configure()
if err != nil {
break
}
@@ -96,7 +96,7 @@ func (nc *nodeConfigurator) apply(rootConfigDir, rootNodeDir string) (err error)
nodeDir.saveConfig()
}
- if err == nil {
+ if err == nil && nc.dnsName != "" {
fmt.Fprint(os.Stdout, "... registering DNS / SRV records\n")
err = nc.registerDNSRecords()
}
diff --git a/netdeploy/remote/nodecfg/nodeDir.go b/netdeploy/remote/nodecfg/nodeDir.go
index 113687545..59f2eba50 100644
--- a/netdeploy/remote/nodecfg/nodeDir.go
+++ b/netdeploy/remote/nodecfg/nodeDir.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -52,7 +52,7 @@ type nodeDir struct {
// * EnableBlockStats
// * DashboardEndpoint
// * DeadlockOverride
-func (nd *nodeDir) configure(dnsName string) (err error) {
+func (nd *nodeDir) configure() (err error) {
fmt.Fprintf(os.Stdout, "Configuring Node %s\n", nd.Name)
if err = nd.configureRelay(nd.IsRelay()); err != nil {
fmt.Fprintf(os.Stdout, "Error during configureRelay: %s\n", err)
diff --git a/netdeploy/remote/topology.go b/netdeploy/remote/topology.go
index b909abda5..1f0b6d736 100644
--- a/netdeploy/remote/topology.go
+++ b/netdeploy/remote/topology.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/connPerfMon.go b/network/connPerfMon.go
index d1b2e7219..e74614ae1 100644
--- a/network/connPerfMon.go
+++ b/network/connPerfMon.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/connPerfMon_test.go b/network/connPerfMon_test.go
index b323e1ec2..a8398b0f3 100644
--- a/network/connPerfMon_test.go
+++ b/network/connPerfMon_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/dialer.go b/network/dialer.go
index 8d7c18aaa..0dc3619d4 100644
--- a/network/dialer.go
+++ b/network/dialer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/limited_reader_slurper.go b/network/limited_reader_slurper.go
index 8795579f4..2bdbb756a 100644
--- a/network/limited_reader_slurper.go
+++ b/network/limited_reader_slurper.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/limited_reader_slurper_test.go b/network/limited_reader_slurper_test.go
index 517069348..92509d95f 100644
--- a/network/limited_reader_slurper_test.go
+++ b/network/limited_reader_slurper_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/messageFilter.go b/network/messageFilter.go
index c497b2770..b42651f46 100644
--- a/network/messageFilter.go
+++ b/network/messageFilter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/messageFilter_test.go b/network/messageFilter_test.go
index 15ce99b9a..1df0e2c00 100644
--- a/network/messageFilter_test.go
+++ b/network/messageFilter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/messagetracer/graphtrace.go b/network/messagetracer/graphtrace.go
index 68c465914..5438c7834 100644
--- a/network/messagetracer/graphtrace.go
+++ b/network/messagetracer/graphtrace.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/messagetracer/interface.go b/network/messagetracer/interface.go
index 567a7860b..e684cc179 100644
--- a/network/messagetracer/interface.go
+++ b/network/messagetracer/interface.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/msgCompressor.go b/network/msgCompressor.go
index 463e38f55..a46f37f2c 100644
--- a/network/msgCompressor.go
+++ b/network/msgCompressor.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/msgCompressor_test.go b/network/msgCompressor_test.go
index 0a8713c87..5e3b927f9 100644
--- a/network/msgCompressor_test.go
+++ b/network/msgCompressor_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/msgOfInterest.go b/network/msgOfInterest.go
index 2345ab20f..c7a3faa12 100644
--- a/network/msgOfInterest.go
+++ b/network/msgOfInterest.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/msgOfInterest_test.go b/network/msgOfInterest_test.go
index 971886b54..c8c8dfda3 100644
--- a/network/msgOfInterest_test.go
+++ b/network/msgOfInterest_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/multiplexer.go b/network/multiplexer.go
index cc25efd3e..fe5b3dcf4 100644
--- a/network/multiplexer.go
+++ b/network/multiplexer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/multiplexer_test.go b/network/multiplexer_test.go
index 58ce88bec..1d0215b90 100644
--- a/network/multiplexer_test.go
+++ b/network/multiplexer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/netprio.go b/network/netprio.go
index bc512afa0..378bea4c0 100644
--- a/network/netprio.go
+++ b/network/netprio.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/netprio_test.go b/network/netprio_test.go
index ff02d4abb..4fc920e8c 100644
--- a/network/netprio_test.go
+++ b/network/netprio_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/peersheap.go b/network/peersheap.go
index dfc572aae..03224b0ea 100644
--- a/network/peersheap.go
+++ b/network/peersheap.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/phonebook.go b/network/phonebook.go
index 656bf9c6d..1ff3ed542 100644
--- a/network/phonebook.go
+++ b/network/phonebook.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/phonebook_test.go b/network/phonebook_test.go
index b1d6c35f4..64f0d7c03 100644
--- a/network/phonebook_test.go
+++ b/network/phonebook_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/rateLimitingTransport.go b/network/rateLimitingTransport.go
index 88c6fec6b..a3fd332cb 100644
--- a/network/rateLimitingTransport.go
+++ b/network/rateLimitingTransport.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/requestLogger.go b/network/requestLogger.go
index b455de85c..562abd9f5 100644
--- a/network/requestLogger.go
+++ b/network/requestLogger.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/requestLogger_test.go b/network/requestLogger_test.go
index b45d985df..a17d054e4 100644
--- a/network/requestLogger_test.go
+++ b/network/requestLogger_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/requestTracker.go b/network/requestTracker.go
index 2ae34c81f..d2f05d8bf 100644
--- a/network/requestTracker.go
+++ b/network/requestTracker.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/requestTracker_test.go b/network/requestTracker_test.go
index 8e9a03eb3..8998b41f7 100644
--- a/network/requestTracker_test.go
+++ b/network/requestTracker_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/topics.go b/network/topics.go
index d9d05dab9..762312585 100644
--- a/network/topics.go
+++ b/network/topics.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/topics_test.go b/network/topics_test.go
index 07b29d09e..b01ede7ff 100644
--- a/network/topics_test.go
+++ b/network/topics_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/wsNetwork.go b/network/wsNetwork.go
index d6778a132..826a7b83f 100644
--- a/network/wsNetwork.go
+++ b/network/wsNetwork.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -724,7 +724,7 @@ func (wn *WebsocketNetwork) setup() {
wn.server.IdleTimeout = httpServerIdleTimeout
wn.server.MaxHeaderBytes = httpServerMaxHeaderBytes
wn.ctx, wn.ctxCancel = context.WithCancel(context.Background())
- wn.relayMessages = wn.config.NetAddress != "" || wn.config.ForceRelayMessages
+ wn.relayMessages = wn.config.IsGossipServer() || wn.config.ForceRelayMessages
if wn.relayMessages || wn.config.ForceFetchTransactions {
wn.wantTXGossip = wantTXGossipYes
}
@@ -798,7 +798,7 @@ func (wn *WebsocketNetwork) Start() {
wn.messagesOfInterestEnc = MarshallMessageOfInterestMap(wn.messagesOfInterest)
}
- if wn.config.NetAddress != "" {
+ if wn.config.IsGossipServer() {
listener, err := net.Listen("tcp", wn.config.NetAddress)
if err != nil {
wn.log.Errorf("network could not listen %v: %s", wn.config.NetAddress, err)
@@ -1182,8 +1182,8 @@ func (wn *WebsocketNetwork) ServeHTTP(response http.ResponseWriter, request *htt
wn.maybeSendMessagesOfInterest(peer, nil)
- peers.Set(float64(wn.NumPeers()))
- incomingPeers.Set(float64(wn.numIncomingPeers()))
+ peers.Set(uint64(wn.NumPeers()))
+ incomingPeers.Set(uint64(wn.numIncomingPeers()))
}
func (wn *WebsocketNetwork) maybeSendMessagesOfInterest(peer *wsPeer, messagesOfInterestEnc []byte) {
@@ -2214,8 +2214,8 @@ func (wn *WebsocketNetwork) tryConnect(addr, gossipAddr string) {
wn.maybeSendMessagesOfInterest(peer, nil)
- peers.Set(float64(wn.NumPeers()))
- outgoingPeers.Set(float64(wn.numOutgoingPeers()))
+ peers.Set(uint64(wn.NumPeers()))
+ outgoingPeers.Set(uint64(wn.numOutgoingPeers()))
if wn.prioScheme != nil {
challenge := response.Header.Get(PriorityChallengeHeader)
@@ -2332,9 +2332,9 @@ func (wn *WebsocketNetwork) removePeer(peer *wsPeer, reason disconnectReason) {
PPCount: atomic.LoadUint64(&peer.ppMessageCount),
})
- peers.Set(float64(wn.NumPeers()))
- incomingPeers.Set(float64(wn.numIncomingPeers()))
- outgoingPeers.Set(float64(wn.numOutgoingPeers()))
+ peers.Set(uint64(wn.NumPeers()))
+ incomingPeers.Set(uint64(wn.numIncomingPeers()))
+ outgoingPeers.Set(uint64(wn.numOutgoingPeers()))
wn.peersLock.Lock()
defer wn.peersLock.Unlock()
@@ -2399,8 +2399,8 @@ func (wn *WebsocketNetwork) countPeersSetGauges() {
numIn++
}
}
- networkIncomingConnections.Set(float64(numIn))
- networkOutgoingConnections.Set(float64(numOut))
+ networkIncomingConnections.Set(uint64(numIn))
+ networkOutgoingConnections.Set(uint64(numOut))
}
func justHost(hostPort string) string {
diff --git a/network/wsNetwork_test.go b/network/wsNetwork_test.go
index 61e067828..726db954c 100644
--- a/network/wsNetwork_test.go
+++ b/network/wsNetwork_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/network/wsPeer.go b/network/wsPeer.go
index 94a1bd2b7..b608d5dd1 100644
--- a/network/wsPeer.go
+++ b/network/wsPeer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -275,6 +275,9 @@ type wsPeer struct {
// clientDataStoreMu synchronizes access to clientDataStore
clientDataStoreMu deadlock.Mutex
+
+ // closers is a slice of functions to run when the peer is closed
+ closers []func()
}
// HTTPPeer is what the opaque Peer might be.
@@ -323,7 +326,8 @@ func (wp *wsPeer) Version() string {
return wp.version
}
-// Unicast sends the given bytes to this specific peer. Does not wait for message to be sent.
+// Unicast sends the given bytes to this specific peer. Does not wait for message to be sent.
+//
// (Implements UnicastPeer)
func (wp *wsPeer) Unicast(ctx context.Context, msg []byte, tag protocol.Tag) error {
var err error
@@ -843,6 +847,10 @@ func (wp *wsPeer) Close(deadline time.Time) {
wp.net.log.Infof("failed to CloseWithoutFlush to connection for %s", wp.conn.RemoteAddr().String())
}
}
+ // now call all registered closers
+ for _, f := range wp.closers {
+ f()
+ }
}
// CloseAndWait internally calls Close() then waits for all peer activity to stop
@@ -963,6 +971,13 @@ func (wp *wsPeer) pfProposalCompressionSupported() bool {
return wp.features&pfCompressedProposal != 0
}
+func (wp *wsPeer) OnClose(f func()) {
+ if wp.closers == nil {
+ wp.closers = []func(){}
+ }
+ wp.closers = append(wp.closers, f)
+}
+
type peerFeatureFlag int
const pfCompressedProposal peerFeatureFlag = 1
diff --git a/network/wsPeer_test.go b/network/wsPeer_test.go
index 2798a5256..7ed49b17c 100644
--- a/network/wsPeer_test.go
+++ b/network/wsPeer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/node/assemble_test.go b/node/assemble_test.go
index 3a3d3979a..8b0a8afae 100644
--- a/node/assemble_test.go
+++ b/node/assemble_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/node/error.go b/node/error.go
index 02bf72505..d177f0c87 100644
--- a/node/error.go
+++ b/node/error.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/node/impls.go b/node/impls.go
index 2b60cf04b..a33bc6e73 100644
--- a/node/impls.go
+++ b/node/impls.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/node/indexer/db.go b/node/indexer/db.go
index 9c74d4ae1..a46106af0 100644
--- a/node/indexer/db.go
+++ b/node/indexer/db.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/node/indexer/indexer.go b/node/indexer/indexer.go
index c022d48ef..064685fe7 100644
--- a/node/indexer/indexer.go
+++ b/node/indexer/indexer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/node/indexer/indexer_test.go b/node/indexer/indexer_test.go
index d8fbea077..ff1e398a4 100644
--- a/node/indexer/indexer_test.go
+++ b/node/indexer/indexer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/node/netprio.go b/node/netprio.go
index 9057fb328..1bf6c17a1 100644
--- a/node/netprio.go
+++ b/node/netprio.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/node/node.go b/node/node.go
index e87aad933..94bb876d8 100644
--- a/node/node.go
+++ b/node/node.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -42,6 +42,7 @@ import (
"github.com/algorand/go-algorand/data/transactions/verify"
"github.com/algorand/go-algorand/ledger"
"github.com/algorand/go-algorand/ledger/ledgercore"
+ "github.com/algorand/go-algorand/ledger/simulation"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/network"
"github.com/algorand/go-algorand/network/messagetracer"
@@ -89,6 +90,11 @@ type StatusReport struct {
CatchpointCatchupVerifiedKVs uint64
CatchpointCatchupTotalBlocks uint64
CatchpointCatchupAcquiredBlocks uint64
+ UpgradePropose protocol.ConsensusVersion
+ UpgradeApprove bool
+ UpgradeDelay uint64
+ NextProtocolVoteBefore basics.Round
+ NextProtocolApprovals uint64
}
// TimeSinceLastRound returns the time since the last block was approved (locally), or 0 if no blocks seen
@@ -192,8 +198,6 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd
p2pNode.SetPrioScheme(node)
node.net = p2pNode
- accountListener := makeTopAccountListener(log)
-
// load stored data
genesisDir := filepath.Join(rootDir, genesis.ID())
ledgerPathnamePrefix := filepath.Join(genesisDir, config.LedgerFilenamePrefix)
@@ -213,7 +217,7 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd
node.cryptoPool = execpool.MakePool(node)
node.lowPriorityCryptoVerificationPool = execpool.MakeBacklog(node.cryptoPool, 2*node.cryptoPool.GetParallelism(), execpool.LowPriority, node)
node.highPriorityCryptoVerificationPool = execpool.MakeBacklog(node.cryptoPool, 2*node.cryptoPool.GetParallelism(), execpool.HighPriority, node)
- node.ledger, err = data.LoadLedger(node.log, ledgerPathnamePrefix, false, genesis.Proto, genalloc, node.genesisID, node.genesisHash, []ledger.BlockListener{}, cfg)
+ node.ledger, err = data.LoadLedger(node.log, ledgerPathnamePrefix, false, genesis.Proto, genalloc, node.genesisID, node.genesisHash, []ledgercore.BlockListener{}, cfg)
if err != nil {
log.Errorf("Cannot initialize ledger (%s): %v", ledgerPathnamePrefix, err)
return nil, err
@@ -221,14 +225,11 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd
node.transactionPool = pools.MakeTransactionPool(node.ledger.Ledger, cfg, node.log)
- blockListeners := []ledger.BlockListener{
+ blockListeners := []ledgercore.BlockListener{
node.transactionPool,
node,
}
- if node.config.EnableTopAccountsReporting {
- blockListeners = append(blockListeners, &accountListener)
- }
node.ledger.RegisterBlockListeners(blockListeners)
txHandlerOpts := data.TxHandlerOpts{
TxPool: node.transactionPool,
@@ -239,7 +240,11 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd
GenesisHash: node.genesisHash,
Config: cfg,
}
- node.txHandler = data.MakeTxHandler(txHandlerOpts)
+ node.txHandler, err = data.MakeTxHandler(txHandlerOpts)
+ if err != nil {
+ log.Errorf("Cannot initialize TxHandler: %v", err)
+ return nil, err
+ }
// Indexer setup
if cfg.IsIndexerActive && cfg.Archival {
@@ -282,7 +287,11 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd
RandomSource: node,
BacklogPool: node.highPriorityCryptoVerificationPool,
}
- node.agreementService = agreement.MakeService(agreementParameters)
+ node.agreementService, err = agreement.MakeService(agreementParameters)
+ if err != nil {
+ log.Errorf("unable to initialize agreement: %v", err)
+ return nil, err
+ }
node.catchupBlockAuth = blockAuthenticatorImpl{Ledger: node.ledger, AsyncVoteVerifier: agreement.MakeAsyncVoteVerifier(node.lowPriorityCryptoVerificationPool)}
node.catchupService = catchup.MakeService(node.log, node.config, p2pNode, node.ledger, node.catchupBlockAuth, agreementLedger.UnmatchedPendingCertificates, node.lowPriorityCryptoVerificationPool)
@@ -398,7 +407,8 @@ func (node *AlgorandFullNode) startMonitoringRoutines() {
go node.oldKeyDeletionThread(node.ctx.Done())
if node.config.EnableUsageLog {
- go logging.UsageLogThread(node.ctx, node.log, 100*time.Millisecond, nil)
+ node.monitoringRoutinesWaitGroup.Add(1)
+ go logging.UsageLogThread(node.ctx, node.log, 100*time.Millisecond, &node.monitoringRoutinesWaitGroup)
}
}
@@ -513,7 +523,7 @@ func (node *AlgorandFullNode) broadcastSignedTxGroup(txgroup []transactions.Sign
return err
}
- _, err = verify.TxnGroup(txgroup, b, node.ledger.VerifiedTransactionCache(), node.ledger)
+ _, err = verify.TxnGroup(txgroup, &b, node.ledger.VerifiedTransactionCache(), node.ledger)
if err != nil {
node.log.Warnf("malformed transaction: %v", err)
return err
@@ -545,6 +555,13 @@ func (node *AlgorandFullNode) broadcastSignedTxGroup(txgroup []transactions.Sign
return nil
}
+// Simulate speculatively runs a transaction group against the current
+// blockchain state and returns the effects and/or errors that would result.
+func (node *AlgorandFullNode) Simulate(txgroup []transactions.SignedTxn) (vb *ledgercore.ValidatedBlock, missingSignatures bool, err error) {
+ simulator := simulation.MakeSimulator(node.ledger)
+ return simulator.Simulate(txgroup)
+}
+
// ListTxns returns SignedTxns associated with a specific account in a range of Rounds (inclusive).
// TxnWithStatus returns the round in which a particular transaction appeared,
// since that information is not part of the SignedTxn itself.
@@ -708,6 +725,13 @@ func (node *AlgorandFullNode) Status() (s StatusReport, err error) {
s.LastCatchpoint = node.ledger.GetLastCatchpointLabel()
s.SynchronizingTime = node.catchupService.SynchronizingTime()
s.CatchupTime = node.catchupService.SynchronizingTime()
+
+ s.UpgradePropose = b.UpgradeVote.UpgradePropose
+ s.UpgradeApprove = b.UpgradeApprove
+ s.UpgradeDelay = uint64(b.UpgradeVote.UpgradeDelay)
+ s.NextProtocolVoteBefore = b.NextProtocolVoteBefore
+ s.NextProtocolApprovals = b.UpgradeState.NextProtocolApprovals
+
}
return
@@ -729,22 +753,6 @@ func (node *AlgorandFullNode) GenesisHash() crypto.Digest {
return node.genesisHash
}
-// PoolStats returns a PoolStatus structure reporting stats about the transaction pool
-func (node *AlgorandFullNode) PoolStats() PoolStats {
- r := node.ledger.Latest()
- last, err := node.ledger.Block(r)
- if err != nil {
- node.log.Warnf("AlgorandFullNode: could not read ledger's last round: %v", err)
- return PoolStats{}
- }
-
- return PoolStats{
- NumConfirmed: uint64(len(last.Payset)),
- NumOutstanding: uint64(node.transactionPool.PendingCount()),
- NumExpired: uint64(node.transactionPool.NumExpired(r)),
- }
-}
-
// SuggestedFee returns the suggested fee per byte recommended to ensure a new transaction is processed in a timely fashion.
// Caller should set fee to max(MinTxnFee, SuggestedFee() * len(encoded SignedTxn))
func (node *AlgorandFullNode) SuggestedFee() basics.MicroAlgos {
@@ -1009,7 +1017,7 @@ func (node *AlgorandFullNode) txPoolGaugeThread(done <-chan struct{}) {
for true {
select {
case <-ticker.C:
- txPoolGauge.Set(float64(node.transactionPool.PendingCount()))
+ txPoolGauge.Set(uint64(node.transactionPool.PendingCount()))
case <-done:
return
}
diff --git a/node/node_test.go b/node/node_test.go
index 647e77c94..f58471090 100644
--- a/node/node_test.go
+++ b/node/node_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -21,7 +21,9 @@ import (
"math/rand"
"os"
"path/filepath"
+ "runtime"
"strconv"
+ "strings"
"sync"
"testing"
"time"
@@ -245,6 +247,11 @@ func TestInitialSync(t *testing.T) {
t.Skip("Test takes ~25 seconds.")
}
+ if (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") &&
+ strings.ToUpper(os.Getenv("CIRCLECI")) == "TRUE" {
+ t.Skip("Test is too heavy for amd64 builder running in parallel with other packages")
+ }
+
backlogPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, nil)
defer backlogPool.Shutdown()
diff --git a/node/topAccountListener.go b/node/topAccountListener.go
deleted file mode 100644
index a0b90e0c5..000000000
--- a/node/topAccountListener.go
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright (C) 2019-2022 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 node
-
-import (
- "sort"
-
- "github.com/algorand/go-algorand/data/basics"
- "github.com/algorand/go-algorand/data/bookkeeping"
- "github.com/algorand/go-algorand/ledger/ledgercore"
- "github.com/algorand/go-algorand/logging"
- "github.com/algorand/go-algorand/logging/telemetryspec"
- "github.com/algorand/go-algorand/protocol"
-)
-
-const numTopAccounts = 20
-
-type topAccountListener struct {
- log logging.Logger
-
- round basics.Round
-
- onlineCirculation basics.MicroAlgos
-
- totalCirculation basics.MicroAlgos
-
- // Cached between rounds to optimize ledger lookups.
- accounts []basics.AccountDetail
-}
-
-func makeTopAccountListener(log logging.Logger) topAccountListener {
- return topAccountListener{
- log: log,
- // TODO: If needed, increase size of this slice to buffer some accounts beyond the TopN.
- accounts: make([]basics.AccountDetail, 0, numTopAccounts),
- }
-}
-
-func (t *topAccountListener) init(balances basics.BalanceDetail) {
- t.round = balances.Round
- t.onlineCirculation = balances.OnlineMoney
- t.totalCirculation = balances.TotalMoney
- t.accounts = t.accounts[:0]
-
- // TODO: After ledger refactor this might be replaced with a loop processing pages of results from a SQL command.
- t.accounts = updateTopAccounts(t.accounts, balances.Accounts)
-}
-
-// BlockListener event, triggered when the ledger writes a new block.
-func (t *topAccountListener) OnNewBlock(block bookkeeping.Block, delta ledgercore.StateDelta) {
- // XXX revise for new ledger API
- // t.update(block, balances)
-
- // If number of accounts after update is insufficient, do a full re-init
- if len(t.accounts) < numTopAccounts {
- // XXX revise for new ledger API
- // t.init(balances)
- }
-
- t.sendEvent()
-}
-
-// Account cache update logic here.
-func (t *topAccountListener) update(b bookkeeping.Block, balances basics.BalanceDetail) {
- lastRound := t.round
-
- // Update metadata.
- t.round = balances.Round
- t.onlineCirculation = balances.OnlineMoney
- t.totalCirculation = balances.TotalMoney
-
- // Invalidate accounts if a round is missed (this also causes the accounts to be lazily initialized).
- if lastRound+1 != balances.Round {
- t.accounts = t.accounts[:0]
- return
- }
-
- // No transactions to update.
- if len(balances.Accounts) == 0 {
- return
- }
-
- // Lookup map for updated accounts.
- accountSet := make(map[basics.Address]bool)
-
- payset, err := b.DecodePaysetFlat()
- if err != nil {
- return
- }
-
- for _, txad := range payset {
- tx := txad.SignedTxn
- if tx.Txn.Type == protocol.PaymentTx {
- accountSet[tx.Txn.Receiver] = true
- if tx.Txn.CloseRemainderTo != (basics.Address{}) {
- accountSet[tx.Txn.CloseRemainderTo] = true
- }
- }
- accountSet[tx.Txn.Src()] = true
- }
-
- // TODO: This loop may not be needed with the ledger refactor.
- // Since the balance list currently is unrelated to the transaction list, must iterate balances.
- for _, tx := range balances.Accounts {
- accountSet[tx.Address] = true
- }
-
- // Remove any accounts in the updated accountSet (they'll be merged back if necessary)
- t.accounts = removeSome(t.accounts, func(addr basics.AccountDetail) bool { return accountSet[addr.Address] })
-
- // Grab the smallest record after removing modified accounts
- smallestAccountSize := basics.MicroAlgos{Raw: 0}
- if len(t.accounts) != 0 {
- smallestAccountSize = t.accounts[len(t.accounts)-1].Algos
- }
-
- t.accounts = updateTopAccounts(t.accounts, balances.Accounts)
-
- // Truncate any accounts after the smallest balance.
- // This triggers a full re-init if the length falls below 'numTopAccounts'
- for i, acct := range t.accounts {
- if acct.Algos.LessThan(smallestAccountSize) {
- t.accounts = t.accounts[:i]
- return
- }
- }
-}
-
-// Helper method to defragment a slice using a predicate to identify stale entries.
-func removeSome(slice []basics.AccountDetail, predicate func(basics.AccountDetail) bool) []basics.AccountDetail {
- // Remove updated accounts (they'll be merged back in as necessary)
- next, end := 0, 0
- for (next + end) < len(slice) {
- if predicate(slice[next+end]) {
- end++
- } else {
- slice[next] = slice[next+end]
- next++
- }
- }
-
- return slice[:next]
-}
-
-// Merge largest accounts from balances into topN, removing values from topN as necessary.
-// The underlying capacity will not be modified, but the length may increase.
-// Note: Doesn't check for duplicates.
-func updateTopAccounts(topN []basics.AccountDetail, balances []basics.AccountDetail) []basics.AccountDetail {
- for _, account := range balances {
- balance := account.Algos
-
- // Quick check for topN if capacity is reached.
- if account.Status != basics.Online || len(topN) != 0 && len(topN) == cap(topN) && balance.Raw <= topN[len(topN)-1].Algos.Raw {
- continue
- }
-
- // Find insertion point.
- pos := sort.Search(len(topN), func(i int) bool {
- return topN[i].Algos.LessThan(balance)
- })
-
- // Increase capacity if more space is available.
- if len(topN) < cap(topN) {
- topN = topN[:len(topN)+1]
- }
-
- // Shift upper elements and insert
- if pos < len(topN) {
- copy(topN[pos+1:], topN[pos:])
- topN[pos] = account
- }
- }
-
- return topN
-}
-
-// Compile current top account state into a telemetry event, and send it.
-func (t *topAccountListener) sendEvent() {
- // Build accounts object.
- payload := make([]map[string]interface{}, 0)
- fCirculation := float64(t.onlineCirculation.ToUint64())
- for _, account := range t.accounts[:] {
- entry := make(map[string]interface{})
- entry["address"] = account.Address.String()
- entry["balance"] = account.Algos.ToUint64()
- entry["stake"] = float64(account.Algos.ToUint64()) / fCirculation
- payload = append(payload, entry)
- }
-
- // Send it out
- t.log.EventWithDetails(telemetryspec.Accounts, telemetryspec.TopAccountsEvent,
- telemetryspec.TopAccountEventDetails{
- Round: uint64(t.round),
- OnlineAccounts: payload,
- OnlineCirculation: t.onlineCirculation.ToUint64(),
- OfflineCirculation: t.totalCirculation.ToUint64() - t.onlineCirculation.ToUint64(),
- })
-}
diff --git a/node/topAccountListener_test.go b/node/topAccountListener_test.go
deleted file mode 100644
index 2c25f85f7..000000000
--- a/node/topAccountListener_test.go
+++ /dev/null
@@ -1,368 +0,0 @@
-// Copyright (C) 2019-2022 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 node
-
-import (
- "fmt"
- "testing"
-
- "github.com/algorand/go-algorand/crypto"
- "github.com/algorand/go-algorand/data/basics"
- "github.com/algorand/go-algorand/data/bookkeeping"
- "github.com/algorand/go-algorand/data/transactions"
- "github.com/algorand/go-algorand/logging"
- "github.com/algorand/go-algorand/protocol"
- "github.com/algorand/go-algorand/test/partitiontest"
-)
-
-// errorString is a trivial implementation of error.
-type errorString struct {
- s string
-}
-
-func (e *errorString) Error() string {
- return e.s
-}
-
-func TestUpdateTopAccounts(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- var topN []basics.AccountDetail
- var input []basics.AccountDetail
-
- // Empty target array.
- topN = []basics.AccountDetail{}
- input = []basics.AccountDetail{onlineDetail(byte(0), 1), onlineDetail(byte(1), 10)}
- topN = updateTopAccounts(topN, input)
-
- if len(topN) != 0 {
- t.Errorf("Target slice not 0: len(topN) == %d", len(topN))
- }
-
- // Extra space available
- topN = make([]basics.AccountDetail, 0, 20)
- input = []basics.AccountDetail{onlineDetail(byte(0), 1), onlineDetail(byte(1), 10)}
- topN = updateTopAccounts(topN, input)
-
- if err := verifyAccountBalances([]uint64{10, 1}, topN); err != nil {
- t.Error(err)
- }
-
- // Overflow, unmodified
- topN = make([]basics.AccountDetail, 0, 4)
- input = []basics.AccountDetail{
- onlineDetail(byte(0), 11),
- onlineDetail(byte(1), 12),
- onlineDetail(byte(2), 13),
- onlineDetail(byte(3), 14),
- onlineDetail(byte(4), 1),
- }
- topN = updateTopAccounts(topN, input)
-
- if err := verifyAccountBalances([]uint64{14, 13, 12, 11}, topN); err != nil {
- t.Error(err)
- }
-
- // Overflow, insert front
- topN = make([]basics.AccountDetail, 0, 4)
- input = []basics.AccountDetail{
- onlineDetail(byte(1), 11),
- onlineDetail(byte(2), 12),
- onlineDetail(byte(3), 13),
- onlineDetail(byte(4), 14),
- onlineDetail(byte(5), 15),
- }
- topN = updateTopAccounts(topN, input)
-
- if err := verifyAccountBalances([]uint64{15, 14, 13, 12}, topN); err != nil {
- t.Error(err)
- }
-
- // Overflow, insert middle
- topN = make([]basics.AccountDetail, 0, 4)
- input = []basics.AccountDetail{
- onlineDetail(byte(1), 11),
- onlineDetail(byte(2), 12),
- onlineDetail(byte(3), 13),
- onlineDetail(byte(4), 15),
- onlineDetail(byte(5), 14),
- }
- topN = updateTopAccounts(topN, input)
-
- if err := verifyAccountBalances([]uint64{15, 14, 13, 12}, topN); err != nil {
- t.Error(err)
- }
-
- // Overflow, insert end
- topN = make([]basics.AccountDetail, 0, 4)
- input = []basics.AccountDetail{
- onlineDetail(byte(1), 11),
- onlineDetail(byte(2), 13),
- onlineDetail(byte(3), 14),
- onlineDetail(byte(4), 15),
- onlineDetail(byte(5), 12),
- }
- topN = updateTopAccounts(topN, input)
-
- if err := verifyAccountBalances([]uint64{15, 14, 13, 12}, topN); err != nil {
- t.Error(err)
- }
-
- // Ignore offline account, shouldn't change topN
- topN = updateTopAccounts(topN, []basics.AccountDetail{detail(byte(6), 200, false)})
- topN = make([]basics.AccountDetail, 0, 4)
- input = []basics.AccountDetail{
- onlineDetail(byte(1), 12),
- onlineDetail(byte(2), 13),
- onlineDetail(byte(3), 14),
- onlineDetail(byte(4), 15),
- detail(byte(5), 200, false),
- }
- topN = updateTopAccounts(topN, input)
-
- if err := verifyAccountBalances([]uint64{15, 14, 13, 12}, topN); err != nil {
- t.Error(err)
- }
-}
-
-func TestRemoveSome(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- // Initialize slice with 100 accounts
- var accountsSlice []basics.AccountDetail
- for i := 0; i <= 100; i++ {
- accountsSlice = append(accountsSlice, onlineDetail(byte(i), 10))
- }
-
- // Remove accounts where the first byte is divisible by 10 (which includes the first and last index
- remove10s := func(details basics.AccountDetail) bool {
- return getInt(details)%10 == 0
- }
-
- accountsSlice = removeSome(accountsSlice, remove10s)
-
- if len(accountsSlice) != 90 {
- t.Errorf("Unexpected size found after removeSome/remove10s: 90 != %d", len(accountsSlice))
- }
- for _, d := range accountsSlice {
- if getInt(d)%10 == 0 {
- t.Errorf("Unexpected value found after removeSome/remove10s: %d", getInt(d))
- }
- }
-
- // Remove remaining accounts where the first byte is even
- removeEven := func(details basics.AccountDetail) bool {
- return getInt(details)%2 == 0
- }
-
- accountsSlice = removeSome(accountsSlice, removeEven)
-
- if len(accountsSlice) != 50 {
- t.Errorf("Unexpected size found after removeSome/removeEven: 50 != %d", len(accountsSlice))
- }
- for _, d := range accountsSlice {
- if getInt(d)%2 == 0 {
- t.Errorf("Unexpected value found after removeSome/removeEven: %d", getInt(d))
- }
- }
-}
-
-func TestUpdate(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- listener := topAccountListener{
- accounts: []basics.AccountDetail{},
- round: 1,
- totalCirculation: basics.MicroAlgos{Raw: 100},
- onlineCirculation: basics.MicroAlgos{Raw: 100},
- }
-
- balanceUpdate := basics.BalanceDetail{
- Accounts: []basics.AccountDetail{},
- Round: 2,
- OnlineMoney: basics.MicroAlgos{Raw: 100000},
- TotalMoney: basics.MicroAlgos{Raw: 1000000},
- }
-
- // Update when accounts is empty.
- listener.update(bookkeeping.Block{}, balanceUpdate)
- if err := verifyListener(listener, []uint64{}, 100000, 1000000, 2); err != nil {
- t.Error(err)
- }
-
- // Transactions causing acct 1 to increase reorders the TopN.
- listener.accounts = []basics.AccountDetail{
- onlineDetail(byte(0), 15),
- onlineDetail(byte(1), 10),
- onlineDetail(byte(2), 5),
- }
- balanceUpdate.Accounts = []basics.AccountDetail{onlineDetail(byte(1), 100)}
- balanceUpdate.Round++
- block := makeBlockWithTxnFor([]byte{3}, []byte{1})
-
- listener.update(block, balanceUpdate)
-
- // 10 -> 100.
- if err := verifyListener(listener, []uint64{100, 15, 5}, 100000, 1000000, 3); err != nil {
- t.Error(err)
- }
-
- // Transactions causing acct 1 to decrease and falls off topN truncates result.
- listener.accounts = []basics.AccountDetail{
- onlineDetail(byte(0), 15),
- onlineDetail(byte(1), 10),
- onlineDetail(byte(2), 5),
- }
- balanceUpdate.Round++
- balanceUpdate.TotalMoney = basics.MicroAlgos{Raw: 99999999}
- balanceUpdate.Accounts = []basics.AccountDetail{onlineDetail(byte(1), 1)}
- block = makeBlockWithTxnFor([]byte{3}, []byte{1})
- listener.update(block, balanceUpdate)
-
- if err := verifyListener(listener, []uint64{15, 5}, 100000, 99999999, 4); err != nil {
- t.Error(err)
- }
-
- // Transactions causing adding a balance to a new account are not reflected in TopN, because they are smaller than
- // the smallest value in TopN (even though there is capacity for it).
- listener.accounts = make([]basics.AccountDetail, 3, 10)
- listener.accounts[0] = onlineDetail(byte(0), 15)
- listener.accounts[1] = onlineDetail(byte(1), 10)
- listener.accounts[2] = onlineDetail(byte(2), 5)
-
- balanceUpdate.Round++
- balanceUpdate.Accounts = []basics.AccountDetail{onlineDetail(byte(3), 1)}
- block = makeBlockWithTxnFor([]byte{5}, []byte{3})
- listener.update(block, balanceUpdate)
-
- if err := verifyListener(listener, []uint64{15, 10, 5}, 100000, 99999999, 5); err != nil {
- t.Error(err)
- }
-
- // Invalid round truncates accounts slice
- listener.update(block, balanceUpdate)
- if len(listener.accounts) != 0 {
- t.Errorf("Accounts should be truncated to zero after unexpected round: len(topN) = %d", len(listener.accounts))
- }
-}
-
-func TestInit(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- listener := makeTopAccountListener(logging.Base())
-
- // "init" should remove existing values before adding new ones.
- balanceUpdate := basics.BalanceDetail{
- Accounts: make([]basics.AccountDetail, 0, 10),
- Round: 2,
- OnlineMoney: basics.MicroAlgos{Raw: 100},
- TotalMoney: basics.MicroAlgos{Raw: 100},
- }
-
- listener.accounts = append(listener.accounts, onlineDetail(byte(10), 100))
- balanceUpdate.Accounts = []basics.AccountDetail{onlineDetail(byte(1), 1)}
-
- listener.init(balanceUpdate)
-
- if err := verifyListener(listener, []uint64{1}, 100, 100, 2); err != nil {
- t.Error(err)
- }
-}
-
-func makeBlockWithTxnFor(senders []byte, receivers []byte) bookkeeping.Block {
- var blk bookkeeping.Block
- blk.BlockHeader.GenesisID = "foo"
- crypto.RandBytes(blk.BlockHeader.GenesisHash[:])
- blk.CurrentProtocol = protocol.ConsensusFuture
-
- paysets := make([]transactions.SignedTxnInBlock, 0, len(receivers))
- for i, b := range receivers {
- txib, err := blk.EncodeSignedTxn(transactions.SignedTxn{
- Txn: transactions.Transaction{
- Type: protocol.PaymentTx,
- Header: transactions.Header{
- Sender: basics.Address{senders[i]},
- GenesisID: blk.BlockHeader.GenesisID,
- GenesisHash: blk.BlockHeader.GenesisHash,
- },
- PaymentTxnFields: transactions.PaymentTxnFields{
- Receiver: basics.Address{b},
- // If this ends up being used by topAccountListener, add it here.
- // Amount: basics.MicroAlgos{123},
- },
- }}, transactions.ApplyData{})
- if err != nil {
- panic(err)
- }
-
- paysets = append(paysets, txib)
- }
-
- blk.Payset = paysets
- return blk
-}
-
-// Helpers for working with data objects.
-func onlineDetail(b byte, bal uint64) basics.AccountDetail {
- return detail(b, bal, true)
-}
-
-func detail(b byte, bal uint64, isOnline bool) basics.AccountDetail {
- state := basics.Offline
- if isOnline {
- state = basics.Online
- }
- return basics.AccountDetail{
- Address: basics.Address{b},
- Algos: basics.MicroAlgos{Raw: bal},
- Status: state,
- }
-}
-
-func getInt(detail basics.AccountDetail) uint64 {
- return uint64([32]byte(detail.Address)[0])
-}
-
-func verifyAccountBalances(expected []uint64, actual []basics.AccountDetail) error {
- if len(expected) != len(actual) {
- return &errorString{fmt.Sprintf("Lengths do not equal: expected(%d) != actual(%d)", len(expected), len(actual))}
- }
-
- for i, a := range actual {
- if expected[i] != a.Algos.Raw {
- return &errorString{fmt.Sprintf("Unexpected result at actual[%d]: expected(%d) != actual(%d)", i, expected[i], a.Algos.Raw)}
- }
- }
-
- return nil
-}
-
-func verifyListener(listener topAccountListener, expected []uint64, online uint64, total uint64, round uint64) error {
- if listener.round != basics.Round(round) {
- return &errorString{fmt.Sprintf("Unexpected round: actual(%d) != expected(%d)", uint64(listener.round), round)}
- }
-
- if listener.onlineCirculation.Raw != online {
- return &errorString{fmt.Sprintf("Unexpected online circulation: actual(%d) != expected(%d)", listener.onlineCirculation.Raw, online)}
- }
-
- if listener.totalCirculation.Raw != total {
- return &errorString{fmt.Sprintf("Unexpected total circulation: actual(%d) != expected(%d)", listener.totalCirculation.Raw, total)}
- }
-
- return verifyAccountBalances(expected, listener.accounts)
-}
diff --git a/nodecontrol/LaggedStdIo.go b/nodecontrol/LaggedStdIo.go
index 4738452f3..1638dd118 100644
--- a/nodecontrol/LaggedStdIo.go
+++ b/nodecontrol/LaggedStdIo.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/nodecontrol/NodeController.go b/nodecontrol/NodeController.go
index b1044b43d..e9ce178b8 100644
--- a/nodecontrol/NodeController.go
+++ b/nodecontrol/NodeController.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/nodecontrol/algodControl.go b/nodecontrol/algodControl.go
index e614cf63f..85c9df30b 100644
--- a/nodecontrol/algodControl.go
+++ b/nodecontrol/algodControl.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/nodecontrol/algodControl_test.go b/nodecontrol/algodControl_test.go
index 80c231ba7..d81d24d9e 100644
--- a/nodecontrol/algodControl_test.go
+++ b/nodecontrol/algodControl_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/nodecontrol/kmdControl.go b/nodecontrol/kmdControl.go
index f55b16876..3c8936783 100644
--- a/nodecontrol/kmdControl.go
+++ b/nodecontrol/kmdControl.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/nodecontrol/kmdControl_common.go b/nodecontrol/kmdControl_common.go
index 151ea7963..9e4d96fea 100644
--- a/nodecontrol/kmdControl_common.go
+++ b/nodecontrol/kmdControl_common.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/nodecontrol/kmdControl_windows.go b/nodecontrol/kmdControl_windows.go
index fbb34a296..2273295e0 100644
--- a/nodecontrol/kmdControl_windows.go
+++ b/nodecontrol/kmdControl_windows.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/nodecontrol/nodeControlErrors.go b/nodecontrol/nodeControlErrors.go
index 6ed43d56a..8502b252d 100644
--- a/nodecontrol/nodeControlErrors.go
+++ b/nodecontrol/nodeControlErrors.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/codec.go b/protocol/codec.go
index 3bd72eb69..e0386eb9b 100644
--- a/protocol/codec.go
+++ b/protocol/codec.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/codec_test.go b/protocol/codec_test.go
index e623f9024..d11a47a90 100644
--- a/protocol/codec_test.go
+++ b/protocol/codec_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/codec_tester.go b/protocol/codec_tester.go
index 8d784a069..59a712e55 100644
--- a/protocol/codec_tester.go
+++ b/protocol/codec_tester.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/consensus.go b/protocol/consensus.go
index f9ee2ec0c..00857875d 100644
--- a/protocol/consensus.go
+++ b/protocol/consensus.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/encodebench_test.go b/protocol/encodebench_test.go
index 3c015bf6b..32267a5d4 100644
--- a/protocol/encodebench_test.go
+++ b/protocol/encodebench_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/hash.go b/protocol/hash.go
index 2d7d48acd..079333a43 100644
--- a/protocol/hash.go
+++ b/protocol/hash.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/networks.go b/protocol/networks.go
index ff1c8ee0f..a6e3b13d1 100644
--- a/protocol/networks.go
+++ b/protocol/networks.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/stateproof.go b/protocol/stateproof.go
index 6031b97d4..4e634fc37 100644
--- a/protocol/stateproof.go
+++ b/protocol/stateproof.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/tags.go b/protocol/tags.go
index 01aee9126..abe947b65 100644
--- a/protocol/tags.go
+++ b/protocol/tags.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/test/allocbound_slice.go b/protocol/test/allocbound_slice.go
index c825a7d25..1d792d568 100644
--- a/protocol/test/allocbound_slice.go
+++ b/protocol/test/allocbound_slice.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/transcode/core.go b/protocol/transcode/core.go
index 863fbabf9..eaf6d00e0 100644
--- a/protocol/transcode/core.go
+++ b/protocol/transcode/core.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/transcode/core_test.go b/protocol/transcode/core_test.go
index e9cfc42f8..34afe8f54 100644
--- a/protocol/transcode/core_test.go
+++ b/protocol/transcode/core_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/protocol/txntype.go b/protocol/txntype.go
index 434255896..d0cfe058b 100644
--- a/protocol/txntype.go
+++ b/protocol/txntype.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/blockService.go b/rpcs/blockService.go
index 25fd16143..cd4a33ad2 100644
--- a/rpcs/blockService.go
+++ b/rpcs/blockService.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/blockService_test.go b/rpcs/blockService_test.go
index 2adb2f1b2..fa934feeb 100644
--- a/rpcs/blockService_test.go
+++ b/rpcs/blockService_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/httpTxSync.go b/rpcs/httpTxSync.go
index 63b99c8a6..6f42eeaac 100644
--- a/rpcs/httpTxSync.go
+++ b/rpcs/httpTxSync.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/ledgerService.go b/rpcs/ledgerService.go
index 582dd89b4..b7af358a3 100644
--- a/rpcs/ledgerService.go
+++ b/rpcs/ledgerService.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/registrar.go b/rpcs/registrar.go
index 1e782d802..f11919329 100644
--- a/rpcs/registrar.go
+++ b/rpcs/registrar.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/txService.go b/rpcs/txService.go
index 654a8e69f..3083e2d33 100644
--- a/rpcs/txService.go
+++ b/rpcs/txService.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/txService_test.go b/rpcs/txService_test.go
index 70e13fc22..dd999d6e6 100644
--- a/rpcs/txService_test.go
+++ b/rpcs/txService_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/txSyncer.go b/rpcs/txSyncer.go
index 399637169..c724417ea 100644
--- a/rpcs/txSyncer.go
+++ b/rpcs/txSyncer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/rpcs/txSyncer_test.go b/rpcs/txSyncer_test.go
index 377080ae8..b05e050ee 100644
--- a/rpcs/txSyncer_test.go
+++ b/rpcs/txSyncer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/scripts/compute_branch.sh b/scripts/compute_branch.sh
index f0ff025d9..211be0521 100755
--- a/scripts/compute_branch.sh
+++ b/scripts/compute_branch.sh
@@ -1,18 +1,21 @@
#!/usr/bin/env bash
-if [[ -n $(git status --porcelain) ]]; then
- # If the branch isn't clean, default to HEAD to match old behavior.
- BRANCH="HEAD"
-elif [ -z "${TRAVIS_BRANCH}" ]; then
- # if there is no travis branch, set based on tag or branch
- case "$(git describe --tags)" in
- *"beta") BRANCH="rel/beta" ;;
- *"stable") BRANCH="rel/stable" ;;
- *"nightly") BRANCH="rel/nightly" ;;
- *) BRANCH=$(git rev-parse --abbrev-ref HEAD)
- esac
-else
- BRANCH="${TRAVIS_BRANCH}"
+BRANCH="${BRANCH:-}"
+if [ -z "$BRANCH" ]; then
+ if [[ -n $(git status --porcelain) ]]; then
+ # If the branch isn't clean, default to HEAD to match old behavior.
+ BRANCH="HEAD"
+ elif [ -z "${TRAVIS_BRANCH}" ]; then
+ # if there is no travis branch, set based on tag or branch
+ case "$(git describe --tags)" in
+ *"beta") BRANCH="rel/beta" ;;
+ *"stable") BRANCH="rel/stable" ;;
+ *"nightly") BRANCH="rel/nightly" ;;
+ *) BRANCH=$(git rev-parse --abbrev-ref HEAD) ;;
+ esac
+ else
+ BRANCH="${TRAVIS_BRANCH}"
+ fi
fi
echo "${BRANCH}"
diff --git a/shared/algoh/config.go b/shared/algoh/config.go
index 687fd1be2..a4cec444d 100644
--- a/shared/algoh/config.go
+++ b/shared/algoh/config.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/shared/pingpong/accounts.go b/shared/pingpong/accounts.go
index 982c768aa..ea9d74cc0 100644
--- a/shared/pingpong/accounts.go
+++ b/shared/pingpong/accounts.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/shared/pingpong/accounts_test.go b/shared/pingpong/accounts_test.go
index 7f2f0a737..08def8994 100644
--- a/shared/pingpong/accounts_test.go
+++ b/shared/pingpong/accounts_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -22,6 +22,8 @@ import (
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
+
+ "github.com/algorand/go-algorand/test/partitiontest"
"github.com/stretchr/testify/assert"
)
@@ -33,6 +35,9 @@ func makeKeyFromSeed(i uint64) *crypto.SignatureSecrets {
}
func TestDeterministicAccounts(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
initCfg := PpConfig{
NumPartAccounts: 20,
DeterministicKeys: true,
diff --git a/shared/pingpong/config.go b/shared/pingpong/config.go
index b5dd17bc5..32f5fab7e 100644
--- a/shared/pingpong/config.go
+++ b/shared/pingpong/config.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -48,6 +48,7 @@ type PpConfig struct {
Quiet bool
RandomNote bool
RandomLease bool
+ TotalLatencyOut string
Program []byte
LogicArgs [][]byte
diff --git a/shared/pingpong/pingpong.go b/shared/pingpong/pingpong.go
index c6a24f493..32352bcfc 100644
--- a/shared/pingpong/pingpong.go
+++ b/shared/pingpong/pingpong.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -14,13 +14,19 @@
// 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 pingpong provides a transaction generating utility for performance testing.
+//
+//nolint:unused,structcheck,deadcode,varcheck // ignore unused pingpong code
package pingpong
import (
+ "bufio"
+ "compress/gzip"
"context"
"encoding/binary"
"errors"
"fmt"
+ "io"
"math"
"math/rand"
"os"
@@ -34,6 +40,7 @@ import (
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model"
"github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
"github.com/algorand/go-algorand/libgoal"
@@ -128,6 +135,11 @@ func (ppa *pingPongAccount) String() string {
return ow.String()
}
+type txidSendTime struct {
+ txid string
+ when time.Time
+}
+
// WorkerState object holds a running pingpong worker
type WorkerState struct {
cfg PpConfig
@@ -149,6 +161,11 @@ type WorkerState struct {
refreshPos int
client *libgoal.Client
+
+ // TotalLatencyOut stuff
+ sentTxid chan txidSendTime
+ latencyBlocks chan bookkeeping.Block
+ latencyOuts []io.Writer // latencyOuts is a chain of *os.File, gzip, etc. Write to last element. .Close() last to first.
}
// returns the number of boxes per app
@@ -345,6 +362,25 @@ func (pps *WorkerState) schedule(n int) {
//fmt.Printf("schedule now=%s next=%s\n", now, pps.nextSendTime)
}
+func (pps *WorkerState) recordTxidSent(txid string, err error) {
+ if err != nil {
+ return
+ }
+ if pps.sentTxid == nil {
+ return
+ }
+ rec := txidSendTime{
+ txid: txid,
+ when: time.Now(),
+ }
+ select {
+ case pps.sentTxid <- rec:
+ // ok!
+ default:
+ // drop, oh well
+ }
+}
+
func (pps *WorkerState) fundAccounts(client *libgoal.Client) error {
var srcFunds, minFund uint64
var err error
@@ -545,6 +581,9 @@ func (pps *WorkerState) RunPingPong(ctx context.Context, ac *libgoal.Client) {
// error = fundAccounts()
// }
+ if pps.cfg.TotalLatencyOut != "" {
+ pps.startTxLatency(ctx, ac)
+ }
pps.nextSendTime = time.Now()
ac.SetSuggestedParamsCacheAge(200 * time.Millisecond)
pps.client = ac
@@ -773,7 +812,9 @@ func (pps *WorkerState) sendFromTo(
sentCount++
pps.schedule(1)
- _, sendErr = client.BroadcastTransaction(stxn)
+ var txid string
+ txid, sendErr = client.BroadcastTransaction(stxn)
+ pps.recordTxidSent(txid, sendErr)
} else {
// Generate txn group
@@ -844,6 +885,8 @@ func (pps *WorkerState) sendFromTo(
sentCount += uint64(len(txGroup))
pps.schedule(len(txGroup))
sendErr = client.BroadcastTransactionGroup(stxGroup)
+ txid := txGroup[0].ID().String()
+ pps.recordTxidSent(txid, sendErr)
}
if sendErr != nil {
@@ -1298,3 +1341,152 @@ func signTxn(signer *pingPongAccount, txn transactions.Transaction, cfg PpConfig
}
return
}
+
+func (pps *WorkerState) startTxLatency(ctx context.Context, ac *libgoal.Client) {
+ fout, err := os.Create(pps.cfg.TotalLatencyOut)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %v", pps.cfg.TotalLatencyOut, err)
+ return
+ }
+ pps.latencyOuts = append(pps.latencyOuts, fout)
+ if strings.HasSuffix(pps.cfg.TotalLatencyOut, ".gz") {
+ gzout := gzip.NewWriter(fout)
+ pps.latencyOuts = append(pps.latencyOuts, gzout)
+ } else {
+ bw := bufio.NewWriter(fout)
+ pps.latencyOuts = append(pps.latencyOuts, bw)
+ }
+ pps.sentTxid = make(chan txidSendTime, 1000)
+ pps.latencyBlocks = make(chan bookkeeping.Block, 1)
+ go pps.txidLatency(ctx)
+ go pps.txidLatencyBlockWaiter(ctx, ac)
+}
+
+type txidSendTimeIndexed struct {
+ txidSendTime
+ index int
+}
+
+const txidLatencySampleSize = 10000
+
+// thread which handles measuring total send-to-commit latency
+func (pps *WorkerState) txidLatency(ctx context.Context) {
+ byTxid := make(map[string]txidSendTimeIndexed, txidLatencySampleSize)
+ txidList := make([]string, 0, txidLatencySampleSize)
+ out := pps.latencyOuts[len(pps.latencyOuts)-1]
+ for {
+ select {
+ case st := <-pps.sentTxid:
+ if len(txidList) < txidLatencySampleSize {
+ index := len(txidList)
+ txidList = append(txidList, st.txid)
+ byTxid[st.txid] = txidSendTimeIndexed{
+ st,
+ index,
+ }
+ } else {
+ // random replacement
+ evict := rand.Intn(len(txidList))
+ delete(byTxid, txidList[evict])
+ txidList[evict] = st.txid
+ byTxid[st.txid] = txidSendTimeIndexed{
+ st,
+ evict,
+ }
+ }
+ case bl := <-pps.latencyBlocks:
+ now := time.Now()
+ txns, err := bl.DecodePaysetFlat()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "block[%d] payset err %v", bl.Round(), err)
+ return
+ }
+ for _, stxn := range txns {
+ txid := stxn.ID().String()
+ st, ok := byTxid[txid]
+ if ok {
+ dt := now.Sub(st.when)
+ fmt.Fprintf(out, "%d\n", dt.Nanoseconds())
+ }
+ }
+ case <-ctx.Done():
+ return
+ }
+ }
+}
+
+type flusher interface {
+ Flush() error
+}
+
+func (pps *WorkerState) txidLatencyDone() {
+ for i := len(pps.latencyOuts); i >= 0; i-- {
+ xo := pps.latencyOuts[i]
+ if fl, ok := xo.(flusher); ok {
+ err := fl.Flush()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %v", pps.cfg.TotalLatencyOut, err)
+ }
+ }
+ if cl, ok := xo.(io.Closer); ok {
+ err := cl.Close()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: %v", pps.cfg.TotalLatencyOut, err)
+ }
+ }
+ }
+}
+
+const errRestartTime = time.Second
+
+func (pps *WorkerState) txidLatencyBlockWaiter(ctx context.Context, ac *libgoal.Client) {
+ defer close(pps.latencyBlocks)
+ done := ctx.Done()
+ isDone := func(err error) bool {
+ select {
+ case <-done:
+ return true
+ default:
+ }
+ fmt.Fprintf(os.Stderr, "block waiter st : %v", err)
+ time.Sleep(errRestartTime)
+ return false
+ }
+restart:
+ select {
+ case <-done:
+ return
+ default:
+ }
+ st, err := ac.Status()
+ if err != nil {
+ if isDone(err) {
+ return
+ }
+ goto restart
+ }
+ nextRound := st.LastRound
+ for {
+ select {
+ case <-done:
+ return
+ default:
+ }
+ st, err = ac.WaitForRound(nextRound)
+ if err != nil {
+ if isDone(err) {
+ return
+ }
+ goto restart
+ }
+ bb, err := ac.BookkeepingBlock(st.LastRound)
+ if err != nil {
+ if isDone(err) {
+ return
+ }
+ goto restart
+ }
+ pps.latencyBlocks <- bb
+ nextRound = st.LastRound
+ }
+}
diff --git a/stateproof/abstractions.go b/stateproof/abstractions.go
index 825b5090e..8527060ae 100644
--- a/stateproof/abstractions.go
+++ b/stateproof/abstractions.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/builder.go b/stateproof/builder.go
index 3d14c1f74..fbb6813a9 100644
--- a/stateproof/builder.go
+++ b/stateproof/builder.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/db.go b/stateproof/db.go
index fa10c8c5e..b1883f591 100644
--- a/stateproof/db.go
+++ b/stateproof/db.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/db_test.go b/stateproof/db_test.go
index 8dca09af4..0de517895 100644
--- a/stateproof/db_test.go
+++ b/stateproof/db_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/recovery.go b/stateproof/recovery.go
index 5902a603e..6bd20337f 100644
--- a/stateproof/recovery.go
+++ b/stateproof/recovery.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/signer.go b/stateproof/signer.go
index 697a56e9e..5c762720a 100644
--- a/stateproof/signer.go
+++ b/stateproof/signer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/stateproofMessageGenerator.go b/stateproof/stateproofMessageGenerator.go
index 8befeb72f..92befc631 100644
--- a/stateproof/stateproofMessageGenerator.go
+++ b/stateproof/stateproofMessageGenerator.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/stateproofMessageGenerator_test.go b/stateproof/stateproofMessageGenerator_test.go
index 1b909d775..26c47ec8a 100644
--- a/stateproof/stateproofMessageGenerator_test.go
+++ b/stateproof/stateproofMessageGenerator_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/verify/stateproof.go b/stateproof/verify/stateproof.go
index 66c6f09d4..fa3641c08 100644
--- a/stateproof/verify/stateproof.go
+++ b/stateproof/verify/stateproof.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/verify/stateproof_test.go b/stateproof/verify/stateproof_test.go
index 38a76c2b8..0495729dd 100644
--- a/stateproof/verify/stateproof_test.go
+++ b/stateproof/verify/stateproof_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/worker.go b/stateproof/worker.go
index ca277dac8..857431f04 100644
--- a/stateproof/worker.go
+++ b/stateproof/worker.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/stateproof/worker_test.go b/stateproof/worker_test.go
index 914514117..bf1ec2636 100644
--- a/stateproof/worker_test.go
+++ b/stateproof/worker_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/commandandcontrol/cc_agent/component/agent.go b/test/commandandcontrol/cc_agent/component/agent.go
index fb1bc504a..4f01065b6 100644
--- a/test/commandandcontrol/cc_agent/component/agent.go
+++ b/test/commandandcontrol/cc_agent/component/agent.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/commandandcontrol/cc_agent/component/agent_test.go b/test/commandandcontrol/cc_agent/component/agent_test.go
index 7ca805eb7..b819ab83c 100644
--- a/test/commandandcontrol/cc_agent/component/agent_test.go
+++ b/test/commandandcontrol/cc_agent/component/agent_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/commandandcontrol/cc_agent/component/pingPongComponent.go b/test/commandandcontrol/cc_agent/component/pingPongComponent.go
index 5bb4be890..ffc61fa5d 100644
--- a/test/commandandcontrol/cc_agent/component/pingPongComponent.go
+++ b/test/commandandcontrol/cc_agent/component/pingPongComponent.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/commandandcontrol/cc_agent/main.go b/test/commandandcontrol/cc_agent/main.go
index 136c38f0d..5adcfa562 100644
--- a/test/commandandcontrol/cc_agent/main.go
+++ b/test/commandandcontrol/cc_agent/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/commandandcontrol/cc_client/main.go b/test/commandandcontrol/cc_client/main.go
index 7125ecd31..6b64117b0 100644
--- a/test/commandandcontrol/cc_client/main.go
+++ b/test/commandandcontrol/cc_client/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/commandandcontrol/cc_service/main.go b/test/commandandcontrol/cc_service/main.go
index 26e592e66..fa39f9229 100644
--- a/test/commandandcontrol/cc_service/main.go
+++ b/test/commandandcontrol/cc_service/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/commandandcontrol/lib/ccCommon.go b/test/commandandcontrol/lib/ccCommon.go
index f3db52a51..36cff09ec 100644
--- a/test/commandandcontrol/lib/ccCommon.go
+++ b/test/commandandcontrol/lib/ccCommon.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/algod/cleanup_test.go b/test/e2e-go/cli/algod/cleanup_test.go
index 24b77e7d9..9e690b45a 100644
--- a/test/e2e-go/cli/algod/cleanup_test.go
+++ b/test/e2e-go/cli/algod/cleanup_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/algod/expect/algod_expect_test.go b/test/e2e-go/cli/algod/expect/algod_expect_test.go
index 06d54b9f3..25850d059 100644
--- a/test/e2e-go/cli/algod/expect/algod_expect_test.go
+++ b/test/e2e-go/cli/algod/expect/algod_expect_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/algod/stdstreams_test.go b/test/e2e-go/cli/algod/stdstreams_test.go
index cb335cad6..f2a412752 100644
--- a/test/e2e-go/cli/algod/stdstreams_test.go
+++ b/test/e2e-go/cli/algod/stdstreams_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/algoh/expect/algoh_expect_test.go b/test/e2e-go/cli/algoh/expect/algoh_expect_test.go
index f260a1bd6..8a94200a6 100644
--- a/test/e2e-go/cli/algoh/expect/algoh_expect_test.go
+++ b/test/e2e-go/cli/algoh/expect/algoh_expect_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/goal/account_test.go b/test/e2e-go/cli/goal/account_test.go
index b01eb6fda..fe05ccddb 100644
--- a/test/e2e-go/cli/goal/account_test.go
+++ b/test/e2e-go/cli/goal/account_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/goal/clerk_test.go b/test/e2e-go/cli/goal/clerk_test.go
index 9f112fbeb..165c65e9f 100644
--- a/test/e2e-go/cli/goal/clerk_test.go
+++ b/test/e2e-go/cli/goal/clerk_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/goal/common_test.go b/test/e2e-go/cli/goal/common_test.go
index b9d0a4b0c..a233d8737 100644
--- a/test/e2e-go/cli/goal/common_test.go
+++ b/test/e2e-go/cli/goal/common_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/goal/expect/catchpointCatchupWebProxy/webproxy.go b/test/e2e-go/cli/goal/expect/catchpointCatchupWebProxy/webproxy.go
index 3ba476438..6b4ac0f31 100644
--- a/test/e2e-go/cli/goal/expect/catchpointCatchupWebProxy/webproxy.go
+++ b/test/e2e-go/cli/goal/expect/catchpointCatchupWebProxy/webproxy.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/goal/expect/goal_expect_test.go b/test/e2e-go/cli/goal/expect/goal_expect_test.go
index 55bdf7ce6..b90dd17d4 100644
--- a/test/e2e-go/cli/goal/expect/goal_expect_test.go
+++ b/test/e2e-go/cli/goal/expect/goal_expect_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/goal/node_cleanup_test.go b/test/e2e-go/cli/goal/node_cleanup_test.go
index 7ad3eaab0..ae70c8960 100644
--- a/test/e2e-go/cli/goal/node_cleanup_test.go
+++ b/test/e2e-go/cli/goal/node_cleanup_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/perf/libgoal_test.go b/test/e2e-go/cli/perf/libgoal_test.go
index a98164003..a3e7581c6 100644
--- a/test/e2e-go/cli/perf/libgoal_test.go
+++ b/test/e2e-go/cli/perf/libgoal_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/perf/payment_test.go b/test/e2e-go/cli/perf/payment_test.go
index 1989ac3cb..fc60e16b8 100644
--- a/test/e2e-go/cli/perf/payment_test.go
+++ b/test/e2e-go/cli/perf/payment_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/tealdbg/cdtmock/main.go b/test/e2e-go/cli/tealdbg/cdtmock/main.go
index 9cb3cf072..6d1292e4b 100644
--- a/test/e2e-go/cli/tealdbg/cdtmock/main.go
+++ b/test/e2e-go/cli/tealdbg/cdtmock/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/cli/tealdbg/expect/tealdbg_expect_test.go b/test/e2e-go/cli/tealdbg/expect/tealdbg_expect_test.go
index 72248cfff..aedf8a2d9 100644
--- a/test/e2e-go/cli/tealdbg/expect/tealdbg_expect_test.go
+++ b/test/e2e-go/cli/tealdbg/expect/tealdbg_expect_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/accountPerf/sixMillion_test.go b/test/e2e-go/features/accountPerf/sixMillion_test.go
index b3d6c107a..cc2095dfb 100644
--- a/test/e2e-go/features/accountPerf/sixMillion_test.go
+++ b/test/e2e-go/features/accountPerf/sixMillion_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/catchup/basicCatchup_test.go b/test/e2e-go/features/catchup/basicCatchup_test.go
index ce908b9e6..435393f70 100644
--- a/test/e2e-go/features/catchup/basicCatchup_test.go
+++ b/test/e2e-go/features/catchup/basicCatchup_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/catchup/catchpointCatchup_test.go b/test/e2e-go/features/catchup/catchpointCatchup_test.go
index f745c9e75..e8de54e66 100644
--- a/test/e2e-go/features/catchup/catchpointCatchup_test.go
+++ b/test/e2e-go/features/catchup/catchpointCatchup_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/devmode/devmode_test.go b/test/e2e-go/features/devmode/devmode_test.go
index c356615e9..ec1fa692b 100644
--- a/test/e2e-go/features/devmode/devmode_test.go
+++ b/test/e2e-go/features/devmode/devmode_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/multisig/multisig_test.go b/test/e2e-go/features/multisig/multisig_test.go
index 07c8f9661..3452b1b50 100644
--- a/test/e2e-go/features/multisig/multisig_test.go
+++ b/test/e2e-go/features/multisig/multisig_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/participation/accountParticipationTransitions_test.go b/test/e2e-go/features/participation/accountParticipationTransitions_test.go
index 52f25ec83..e9a4e5735 100644
--- a/test/e2e-go/features/participation/accountParticipationTransitions_test.go
+++ b/test/e2e-go/features/participation/accountParticipationTransitions_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/participation/deletePartKeys_test.go b/test/e2e-go/features/participation/deletePartKeys_test.go
index 5c7755ca7..c72925dba 100644
--- a/test/e2e-go/features/participation/deletePartKeys_test.go
+++ b/test/e2e-go/features/participation/deletePartKeys_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/participation/onlineOfflineParticipation_test.go b/test/e2e-go/features/participation/onlineOfflineParticipation_test.go
index 0a6f23311..ed593b765 100644
--- a/test/e2e-go/features/participation/onlineOfflineParticipation_test.go
+++ b/test/e2e-go/features/participation/onlineOfflineParticipation_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/participation/overlappingParticipationKeys_test.go b/test/e2e-go/features/participation/overlappingParticipationKeys_test.go
index 45ac6c97c..8e4df5fb3 100644
--- a/test/e2e-go/features/participation/overlappingParticipationKeys_test.go
+++ b/test/e2e-go/features/participation/overlappingParticipationKeys_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/participation/participationExpiration_test.go b/test/e2e-go/features/participation/participationExpiration_test.go
index 6f8c0b1a9..3bb915ab0 100644
--- a/test/e2e-go/features/participation/participationExpiration_test.go
+++ b/test/e2e-go/features/participation/participationExpiration_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/participation/participationRewards_test.go b/test/e2e-go/features/participation/participationRewards_test.go
index 5581de195..a9c81f6a4 100644
--- a/test/e2e-go/features/participation/participationRewards_test.go
+++ b/test/e2e-go/features/participation/participationRewards_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/partitionRecovery/partitionRecovery_test.go b/test/e2e-go/features/partitionRecovery/partitionRecovery_test.go
index dc549d72e..3f16be7f6 100644
--- a/test/e2e-go/features/partitionRecovery/partitionRecovery_test.go
+++ b/test/e2e-go/features/partitionRecovery/partitionRecovery_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/stateproofs/stateproofs_test.go b/test/e2e-go/features/stateproofs/stateproofs_test.go
index ac2270cc4..e293006f4 100644
--- a/test/e2e-go/features/stateproofs/stateproofs_test.go
+++ b/test/e2e-go/features/stateproofs/stateproofs_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/teal/compile_test.go b/test/e2e-go/features/teal/compile_test.go
index 866e1c54d..760551f28 100644
--- a/test/e2e-go/features/teal/compile_test.go
+++ b/test/e2e-go/features/teal/compile_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/accountv2_test.go b/test/e2e-go/features/transactions/accountv2_test.go
index 297222934..fc6a82888 100644
--- a/test/e2e-go/features/transactions/accountv2_test.go
+++ b/test/e2e-go/features/transactions/accountv2_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/app_pages_test.go b/test/e2e-go/features/transactions/app_pages_test.go
index f9a56c212..f9ce0878a 100644
--- a/test/e2e-go/features/transactions/app_pages_test.go
+++ b/test/e2e-go/features/transactions/app_pages_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/application_test.go b/test/e2e-go/features/transactions/application_test.go
index 380264b29..a93794bb2 100644
--- a/test/e2e-go/features/transactions/application_test.go
+++ b/test/e2e-go/features/transactions/application_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/asset_test.go b/test/e2e-go/features/transactions/asset_test.go
index 0802308b3..3c6987b42 100644
--- a/test/e2e-go/features/transactions/asset_test.go
+++ b/test/e2e-go/features/transactions/asset_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/close_account_test.go b/test/e2e-go/features/transactions/close_account_test.go
index 8787568dc..3426ea957 100644
--- a/test/e2e-go/features/transactions/close_account_test.go
+++ b/test/e2e-go/features/transactions/close_account_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/group_test.go b/test/e2e-go/features/transactions/group_test.go
index 4852da25d..1d8341f42 100644
--- a/test/e2e-go/features/transactions/group_test.go
+++ b/test/e2e-go/features/transactions/group_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/lease_test.go b/test/e2e-go/features/transactions/lease_test.go
index 10a238e74..7dbb06190 100644
--- a/test/e2e-go/features/transactions/lease_test.go
+++ b/test/e2e-go/features/transactions/lease_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/onlineStatusChange_test.go b/test/e2e-go/features/transactions/onlineStatusChange_test.go
index 2cbfa793a..a8a9ceb01 100644
--- a/test/e2e-go/features/transactions/onlineStatusChange_test.go
+++ b/test/e2e-go/features/transactions/onlineStatusChange_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/proof_test.go b/test/e2e-go/features/transactions/proof_test.go
index bb5175c92..f7d8141d7 100644
--- a/test/e2e-go/features/transactions/proof_test.go
+++ b/test/e2e-go/features/transactions/proof_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/features/transactions/sendReceive_test.go b/test/e2e-go/features/transactions/sendReceive_test.go
index e09c67a6b..1a261a844 100644
--- a/test/e2e-go/features/transactions/sendReceive_test.go
+++ b/test/e2e-go/features/transactions/sendReceive_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/globals/constants.go b/test/e2e-go/globals/constants.go
index b738b6d65..b289379a1 100644
--- a/test/e2e-go/globals/constants.go
+++ b/test/e2e-go/globals/constants.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/kmd/e2e_kmd_server_client_test.go b/test/e2e-go/kmd/e2e_kmd_server_client_test.go
index eb61861bb..7d7977a0b 100644
--- a/test/e2e-go/kmd/e2e_kmd_server_client_test.go
+++ b/test/e2e-go/kmd/e2e_kmd_server_client_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/kmd/e2e_kmd_sqlite_test.go b/test/e2e-go/kmd/e2e_kmd_sqlite_test.go
index 496070ccb..d8290ae5e 100644
--- a/test/e2e-go/kmd/e2e_kmd_sqlite_test.go
+++ b/test/e2e-go/kmd/e2e_kmd_sqlite_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go b/test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go
index 364dcfb03..c5dd733e0 100644
--- a/test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go
+++ b/test/e2e-go/kmd/e2e_kmd_wallet_keyops_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go b/test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go
index be8f27e76..8995b70c7 100644
--- a/test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go
+++ b/test/e2e-go/kmd/e2e_kmd_wallet_multisig_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/kmd/e2e_kmd_wallet_test.go b/test/e2e-go/kmd/e2e_kmd_wallet_test.go
index 4fb8f3a15..558c4ff30 100644
--- a/test/e2e-go/kmd/e2e_kmd_wallet_test.go
+++ b/test/e2e-go/kmd/e2e_kmd_wallet_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/perf/basic_test.go b/test/e2e-go/perf/basic_test.go
index b4044303a..be8a4c819 100644
--- a/test/e2e-go/perf/basic_test.go
+++ b/test/e2e-go/perf/basic_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/restAPI/restClient_test.go b/test/e2e-go/restAPI/restClient_test.go
index 4592fd426..146d1585c 100644
--- a/test/e2e-go/restAPI/restClient_test.go
+++ b/test/e2e-go/restAPI/restClient_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/stress/transactions/createManyAndGoOnline_test.go b/test/e2e-go/stress/transactions/createManyAndGoOnline_test.go
index fc05837ba..a7e5c9755 100644
--- a/test/e2e-go/stress/transactions/createManyAndGoOnline_test.go
+++ b/test/e2e-go/stress/transactions/createManyAndGoOnline_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/upgrades/application_support_test.go b/test/e2e-go/upgrades/application_support_test.go
index 00ee3024a..8746545d0 100644
--- a/test/e2e-go/upgrades/application_support_test.go
+++ b/test/e2e-go/upgrades/application_support_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/upgrades/rekey_support_test.go b/test/e2e-go/upgrades/rekey_support_test.go
index 56f5b6408..4c7878bfa 100644
--- a/test/e2e-go/upgrades/rekey_support_test.go
+++ b/test/e2e-go/upgrades/rekey_support_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/upgrades/send_receive_upgrade_test.go b/test/e2e-go/upgrades/send_receive_upgrade_test.go
index a3992872a..b6b413423 100644
--- a/test/e2e-go/upgrades/send_receive_upgrade_test.go
+++ b/test/e2e-go/upgrades/send_receive_upgrade_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/e2e-go/upgrades/stateproof_participation_test.go b/test/e2e-go/upgrades/stateproof_participation_test.go
index 75d3dad87..d8153dea5 100644
--- a/test/e2e-go/upgrades/stateproof_participation_test.go
+++ b/test/e2e-go/upgrades/stateproof_participation_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/framework/fixtures/baseFixture.go b/test/framework/fixtures/baseFixture.go
index a2205d3c6..918fa6d7d 100644
--- a/test/framework/fixtures/baseFixture.go
+++ b/test/framework/fixtures/baseFixture.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/framework/fixtures/expectFixture.go b/test/framework/fixtures/expectFixture.go
index 35b748968..50206bda1 100644
--- a/test/framework/fixtures/expectFixture.go
+++ b/test/framework/fixtures/expectFixture.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/framework/fixtures/fixture.go b/test/framework/fixtures/fixture.go
index 914cc092e..83bb7d712 100644
--- a/test/framework/fixtures/fixture.go
+++ b/test/framework/fixtures/fixture.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/framework/fixtures/goalFixture.go b/test/framework/fixtures/goalFixture.go
index 576db08ca..ef52b51b6 100644
--- a/test/framework/fixtures/goalFixture.go
+++ b/test/framework/fixtures/goalFixture.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/framework/fixtures/kmdFixture.go b/test/framework/fixtures/kmdFixture.go
index db4794d3d..21c70dd31 100644
--- a/test/framework/fixtures/kmdFixture.go
+++ b/test/framework/fixtures/kmdFixture.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/framework/fixtures/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go
index 02548c1f0..7294bafc4 100644
--- a/test/framework/fixtures/libgoalFixture.go
+++ b/test/framework/fixtures/libgoalFixture.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/framework/fixtures/restClientFixture.go b/test/framework/fixtures/restClientFixture.go
index 5150e5c28..159984354 100644
--- a/test/framework/fixtures/restClientFixture.go
+++ b/test/framework/fixtures/restClientFixture.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/framework/fixtures/webProxyFixture.go b/test/framework/fixtures/webProxyFixture.go
index 0b9e12d93..c04887580 100644
--- a/test/framework/fixtures/webProxyFixture.go
+++ b/test/framework/fixtures/webProxyFixture.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/heapwatch/block_history.py b/test/heapwatch/block_history.py
index ac5c631c4..c7fa3ce6f 100644
--- a/test/heapwatch/block_history.py
+++ b/test/heapwatch/block_history.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (C) 2019-2022 Algorand, Inc.
+# 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
@@ -22,6 +22,7 @@
# pip install py-algorand-sdk
import argparse
+import atexit
import base64
import logging
import os
@@ -231,6 +232,7 @@ def main():
ap.add_argument('-t', '--token', default=None, help='algod API access token')
ap.add_argument('--header', dest='headers', nargs='*', help='"Name: value" HTTP header (repeatable)')
ap.add_argument('--all', default=False, action='store_true', help='fetch all blocks from 0')
+ ap.add_argument('--pid')
ap.add_argument('--verbose', default=False, action='store_true')
ap.add_argument('-o', '--out', default=None, help='file to append json lines to')
args = ap.parse_args()
@@ -240,6 +242,11 @@ def main():
else:
logging.basicConfig(level=logging.INFO)
+ if args.pid:
+ with open(args.pid, 'w') as fout:
+ fout.write('{}'.format(os.getpid()))
+ atexit.register(os.remove, args.pid)
+
algorand_data = args.algod or os.getenv('ALGORAND_DATA')
if not algorand_data and not ((args.token or args.headers) and args.addr):
sys.stderr.write('must specify algod data dir by $ALGORAND_DATA or -d/--algod; OR --a/--addr and -t/--token\n')
diff --git a/test/heapwatch/block_history_plot.py b/test/heapwatch/block_history_plot.py
index 48bd22ebd..03f5a4522 100644
--- a/test/heapwatch/block_history_plot.py
+++ b/test/heapwatch/block_history_plot.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (C) 2019-2022 Algorand, Inc.
+# 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
@@ -25,6 +25,7 @@
import base64
import os
import statistics
+import sys
from algosdk.encoding import msgpack
from matplotlib import pyplot as plt
@@ -65,7 +66,10 @@ def process(path, args):
block = row['block']
rnd = block.get('rnd',0)
if (rnd < minrnd) or ((maxrnd is not None) and (rnd > maxrnd)):
+ sys.stderr.write(f'skip rnd {rnd}\n')
continue
+ if (prevrnd is not None) and (rnd <= prevrnd):
+ sys.stderr.write(f'wat rnd {rnd}, prevrnd {prevrnd}, line {count}\n')
tc = block.get('tc', 0)
ts = block.get('ts', 0) # timestamp recorded at algod, 1s resolution int
_time = row['_time'] # timestamp recorded at client, 0.000001s resolution float
@@ -82,6 +86,8 @@ def process(path, args):
tsv.append(_time)
if dt > 0.5:
dtxn = tc - prevtc
+ if dtxn < 0:
+ sys.stderr.write(f'{path}:{count} tc {tc}, prevtc {prevtc}, rnd {rnd}, prevrnd {prevrnd}\n')
tps = dtxn / dt
mintxn = min(dtxn,mintxn)
maxtxn = max(dtxn,maxtxn)
@@ -93,7 +99,7 @@ def process(path, args):
dtv.append(dt)
txnv.append(dtxn)
else:
- print('b[{}] - b[{}], dt={}'.format(rnd-1,rnd,dt))
+ sys.stderr.write('b[{}] - b[{}], dt={}\n'.format(rnd-1,rnd,dt))
else:
tsv.append(ts)
prevrnd = rnd
diff --git a/test/heapwatch/block_history_relays.py b/test/heapwatch/block_history_relays.py
index 5d3c7b0c7..ebf310587 100644
--- a/test/heapwatch/block_history_relays.py
+++ b/test/heapwatch/block_history_relays.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (C) 2019-2022 Algorand, Inc.
+# 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
diff --git a/test/heapwatch/client_ram_report.py b/test/heapwatch/client_ram_report.py
index 04f212f18..51f7bcb31 100644
--- a/test/heapwatch/client_ram_report.py
+++ b/test/heapwatch/client_ram_report.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (C) 2019-2022 Algorand, Inc.
+# 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
diff --git a/test/heapwatch/heapWatch.py b/test/heapwatch/heapWatch.py
index 883223e05..22a5a0e3a 100644
--- a/test/heapwatch/heapWatch.py
+++ b/test/heapwatch/heapWatch.py
@@ -1,6 +1,6 @@
#!/usr/bin/python3
#
-# Copyright (C) 2019-2022 Algorand, Inc.
+# 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
diff --git a/test/heapwatch/metrics_delta.py b/test/heapwatch/metrics_delta.py
index 3ec493988..d50346ec0 100644
--- a/test/heapwatch/metrics_delta.py
+++ b/test/heapwatch/metrics_delta.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (C) 2019-2022 Algorand, Inc.
+# 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
@@ -304,6 +304,28 @@ class summary:
plt.savefig(outpath + '.svg', format='svg')
plt.savefig(outpath + '.png', format='png')
+ def heap_xy(self):
+ # data from algod_go_memory_classes_heap_objects_bytes
+ x = []
+ y = []
+ for nick, ns in self.nodes.items():
+ if not ns.objectBytes:
+ continue
+ for curtime, nbytes in ns.objectBytes:
+ x.append(curtime)
+ y.append(nbytes)
+ return x, y
+
+ def plot_heaps(self, outpath):
+ # data from algod_go_memory_classes_heap_objects_bytes
+ from matplotlib import pyplot as plt
+ x, y = self.heap_xy()
+ if (not x) or (not y):
+ return
+ plt.scatter(x, y)
+ plt.savefig(outpath + '.svg', format='svg')
+ plt.savefig(outpath + '.png', format='png')
+
def anynickre(nick_re, nicks):
if not nick_re:
return True
@@ -343,6 +365,12 @@ def process_nick_re(nre, filesByNick, nick_to_tfname, rsum, args, grsum):
if anynickre(nretup, (rnick,nick)):
rsum(process_files(args, nick, paths, grsum), nick)
+label_colors = {
+ 'relay': (1.0,0,0),
+ 'pn': (0,0,1.0),
+ 'npn': (.7,.7,0),
+}
+
def main():
os.environ['TZ'] = 'UTC'
time.tzset()
@@ -357,6 +385,7 @@ def main():
ap.add_argument('--nick-re', action='append', default=[], help='regexp to filter node names, may be repeated')
ap.add_argument('--nick-lre', action='append', default=[], help='label:regexp to filter node names, may be repeated')
ap.add_argument('--pool-plot-root', help='write to foo.svg and .png')
+ ap.add_argument('--heap-plot-root', help='write to foo.svg and .png')
ap.add_argument('--verbose', default=False, action='store_true')
args = ap.parse_args()
@@ -435,6 +464,7 @@ def main():
htmlout.write(rsum.html())
return 0
if args.nick_lre:
+ heaps_xy_color = []
for lnre in args.nick_lre:
label, nre = lnre.split(':', maxsplit=1)
rsum = summary(label)
@@ -443,7 +473,18 @@ def main():
print('\n')
if htmlout:
htmlout.write(rsum.html())
+ x, y = rsum.heap_xy()
+ c = label_colors.get(label)
+ heaps_xy_color.append((x, y, c))
+ if args.heap_plot_root:
+ from matplotlib import pyplot as plt
+ for x,y,c in heaps_xy_color:
+ plt.scatter(x, y, color=c)
+ plt.savefig(args.heap_plot_root + '.svg', format='svg')
+ plt.savefig(args.heap_plot_root + '.png', format='png')
return 0
+ elif args.heap_plot_root:
+ grsum.plot_heaps(args.heap_plot_root)
# no filters, print global result
print(grsum)
@@ -497,6 +538,8 @@ class nodestats:
self.times = []
# algod_tx_pool_count{}
self.txPool = []
+ # objectBytes = [(curtime, algod_go_memory_classes_heap_objects_bytes), ...]
+ self.objectBytes = []
# total across all measurements
self.tps = 0
self.blockTime = 0
@@ -506,6 +549,8 @@ class nodestats:
def process_files(self, args, nick=None, metrics_files=None, bisource=None):
"returns self, a nodestats object"
+ if bisource is not None:
+ logger.debug('process_files %r external bisource', nick)
if bisource is None:
bisource = {}
self.args = args
@@ -546,11 +591,15 @@ class nodestats:
with open(bijsonpath, 'rt', encoding="utf-8") as fin:
bi = json.load(fin)
self.biByTime[curtime] = bi
+ logger.debug('bi r=%d %s', bi.get('block',{}).get('rnd', 0), bijsonpath)
if bi is None:
bi = bisource.get(curtime)
if bi is None:
logger.warning('%s no blockinfo', path)
self.txPool.append(cur.get('algod_tx_pool_count{}'))
+ objectBytes = cur.get('algod_go_memory_classes_heap_objects_bytes')
+ if objectBytes:
+ self.objectBytes.append((curtime, objectBytes))
#logger.debug('%s: %r', path, cur)
verifyGood = cur.get('algod_agreement_proposal_verify_good{}')
verifyMs = cur.get('algod_agreement_proposal_verify_ms{}')
@@ -562,7 +611,7 @@ class nodestats:
dt = curtime - prevtime
#print("{} ->\n{}".format(prevPath, path))
#print(json.dumps(d, indent=2, sort_keys=True))
- self.deltas.append((curtime, d))
+ self.deltas.append((curtime, dt, d))
tps = None
blocktime = None
txnCount = 0
@@ -607,8 +656,14 @@ class nodestats:
prevbi = bi
if prevbi is None or firstBi is None:
return self
+ if prevbi is firstBi:
+ logger.warning('only one blockinfo for %s', nick)
+ return self
txnCount = prevbi.get('block',{}).get('tc',0) - firstBi.get('block',{}).get('tc',0)
rounds = prevbi.get('block',{}).get('rnd',0) - firstBi.get('block',{}).get('rnd',0)
+ if rounds == 0:
+ logger.warning('no rounds for %s', nick)
+ return self
totalDt = prevtime - firstTime
self.tps = txnCount / totalDt
self.blockTime = totalDt / rounds
@@ -625,7 +680,7 @@ class nodestats:
reportf.close()
if self.deltas and args.deltas:
keys = set()
- for ct, d in self.deltas:
+ for ct, dt, d in self.deltas:
keys.update(set(d.keys()))
keys = sorted(keys)
deltapath = args.deltas
@@ -633,9 +688,9 @@ class nodestats:
deltapath = deltapath.replace('.csv', '.{}.csv'.format(nick))
with sopen(deltapath, 'wt') as fout:
writer = csv.writer(fout)
- writer.writerow(['when'] + keys)
+ writer.writerow(['when', 'dt'] + keys)
for ct, d in self.deltas:
- row = [time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(ct))]
+ row = [time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(ct)), dt]
for k in keys:
row.append(d.get(k, None))
writer.writerow(row)
diff --git a/test/heapwatch/nodeHostTarget.py b/test/heapwatch/nodeHostTarget.py
index 2a29f4f8f..ccab46ba1 100644
--- a/test/heapwatch/nodeHostTarget.py
+++ b/test/heapwatch/nodeHostTarget.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (C) 2019-2022 Algorand, Inc.
+# 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
diff --git a/test/heapwatch/plot_crr_csv.py b/test/heapwatch/plot_crr_csv.py
index 14f23b857..f4a46cf85 100755
--- a/test/heapwatch/plot_crr_csv.py
+++ b/test/heapwatch/plot_crr_csv.py
@@ -47,6 +47,9 @@ def main():
klist.append((xround, v))
minv = smin(minv, v)
maxv = smax(maxv, v)
+ if not fvals:
+ print(f"{fname} empty")
+ continue
print("{} found series {}".format(fname, sorted(fvals.keys())))
fig, ax = plt.subplots()
ax.set_ylabel('bytes')
diff --git a/test/heapwatch/runNodeHost.py b/test/heapwatch/runNodeHost.py
index 1d14881fd..be2ce61be 100644
--- a/test/heapwatch/runNodeHost.py
+++ b/test/heapwatch/runNodeHost.py
@@ -1,5 +1,5 @@
#!/usr/bin/python3
-# Copyright (C) 2019-2022 Algorand, Inc.
+# 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
diff --git a/test/linttest/lintissues.go b/test/linttest/lintissues.go
index 77b0c33c2..cdfdbf422 100644
--- a/test/linttest/lintissues.go
+++ b/test/linttest/lintissues.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/netperf-go/puppeteer/main.go b/test/netperf-go/puppeteer/main.go
index 87dbb47cf..fbebeb705 100644
--- a/test/netperf-go/puppeteer/main.go
+++ b/test/netperf-go/puppeteer/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/netperf-go/puppeteer/promMetricFetcher.go b/test/netperf-go/puppeteer/promMetricFetcher.go
index 06e0853c8..ea24008fa 100644
--- a/test/netperf-go/puppeteer/promMetricFetcher.go
+++ b/test/netperf-go/puppeteer/promMetricFetcher.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/netperf-go/puppeteer/promMetricFetcher_test.go b/test/netperf-go/puppeteer/promMetricFetcher_test.go
index 86579f86f..200a9af25 100644
--- a/test/netperf-go/puppeteer/promMetricFetcher_test.go
+++ b/test/netperf-go/puppeteer/promMetricFetcher_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/netperf-go/puppeteer/puppeteer.go b/test/netperf-go/puppeteer/puppeteer.go
index 5a5fab11c..260c65967 100644
--- a/test/netperf-go/puppeteer/puppeteer.go
+++ b/test/netperf-go/puppeteer/puppeteer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/netperf-go/puppeteer/puppeteer_test.go b/test/netperf-go/puppeteer/puppeteer_test.go
index 7bf89cddc..e20275143 100644
--- a/test/netperf-go/puppeteer/puppeteer_test.go
+++ b/test/netperf-go/puppeteer/puppeteer_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/netperf-go/puppeteer/roundpoller.go b/test/netperf-go/puppeteer/roundpoller.go
index f4d689f6f..2ce501a2a 100644
--- a/test/netperf-go/puppeteer/roundpoller.go
+++ b/test/netperf-go/puppeteer/roundpoller.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/partitiontest/filtering.go b/test/partitiontest/filtering.go
index 0af5e2e14..19820f601 100644
--- a/test/partitiontest/filtering.go
+++ b/test/partitiontest/filtering.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/reflectionhelpers/helpers.go b/test/reflectionhelpers/helpers.go
index ad551aa0f..79c24d5e2 100644
--- a/test/reflectionhelpers/helpers.go
+++ b/test/reflectionhelpers/helpers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/test/scripts/e2e.sh b/test/scripts/e2e.sh
index b7d04ff4a..760c3170a 100755
--- a/test/scripts/e2e.sh
+++ b/test/scripts/e2e.sh
@@ -78,12 +78,15 @@ echo "RUN_KMD_WITH_UNSAFE_SCRYPT = ${RUN_KMD_WITH_UNSAFE_SCRYPT}"
export BINDIR=${TEMPDIR}/bin
export DATADIR=${TEMPDIR}/data
+export NETWORKDIR=${TEMPDIR}/net
function reset_dirs() {
rm -rf ${BINDIR}
rm -rf ${DATADIR}
+ rm -rf ${NETWORKDIR}
mkdir -p ${BINDIR}
mkdir -p ${DATADIR}
+ mkdir -p ${NETWORKDIR}
}
# $1 - Message
@@ -121,7 +124,6 @@ if [ -z "$E2E_TEST_FILTER" ] || [ "$E2E_TEST_FILTER" == "SCRIPTS" ]; then
python3 -m venv "${TEMPDIR}/ve"
. "${TEMPDIR}/ve/bin/activate"
"${TEMPDIR}/ve/bin/pip3" install --upgrade pip
- "${TEMPDIR}/ve/bin/pip3" install --upgrade cryptograpy
# Pin a version of our python SDK's so that breaking changes don't spuriously break our tests.
# Please update as necessary.
@@ -155,9 +157,16 @@ if [ -z "$E2E_TEST_FILTER" ] || [ "$E2E_TEST_FILTER" == "SCRIPTS" ]; then
exit
fi
- ./timeout 200 ./e2e_basic_start_stop.sh
+ goal network create \
+ -r $NETWORKDIR \
+ -n tbd \
+ -t ../testdata/nettemplates/TwoNodes50EachFuture.json
+
+ ./timeout 200 ./e2e_basic_start_stop.sh $NETWORKDIR
duration "e2e_basic_start_stop.sh"
+ goal network delete -r $NETWORKDIR
+
KEEP_TEMPS_CMD_STR=""
# If the platform is arm64, we want to pass "--keep-temps" into e2e_client_runner.py
diff --git a/test/scripts/e2e_basic_start_stop.sh b/test/scripts/e2e_basic_start_stop.sh
index 1bb9b0bf1..03475acef 100755
--- a/test/scripts/e2e_basic_start_stop.sh
+++ b/test/scripts/e2e_basic_start_stop.sh
@@ -1,24 +1,31 @@
#!/usr/bin/env bash
+
+set -euf -o pipefail
+
echo "######################################################################"
echo " e2e_basic_start_stop"
echo "######################################################################"
-set -e
-# Suppress telemetry reporting for tests
-export ALGOTEST=1
+if [ "$#" -eq 0 ]; then
+ echo "Usage: e2e_basic_start_stop.sh <DATA_DIR>"
+ exit 1
+fi
+DATA_DIR="$1"
RUNNING_COUNT=0
+# Suppress telemetry reporting for tests
+export ALGOTEST=1
function update_running_count() {
- PIDS=($(pgrep -u $(whoami) -x algod)) || true
+ PIDS=($(pgrep -u "$(whoami)" -x algod)) || true
RUNNING_COUNT=${#PIDS[@]}
}
function verify_at_least_one_running() {
# Starting up can take some time, so wait at least 2 seconds
- for TRIES in 1 2 3 4 5; do
+ for _ in 1 2 3 4 5; do
update_running_count
- if [ ${RUNNING_COUNT} -ge 1 ]; then
+ if [ "$RUNNING_COUNT" -ge 1 ]; then
return 0
fi
sleep .4
@@ -28,34 +35,32 @@ function verify_at_least_one_running() {
}
function verify_none_running() {
- local datadir=$1
-
# Shutting down can take some time, so wait at least 5 seconds
- for TRIES in 1 2 3 4 5; do
+ for _ in 1 2 3 4 5; do
update_running_count
- if [ ${RUNNING_COUNT} -eq 0 ]; then
+ if [ "$RUNNING_COUNT" -eq 0 ]; then
return 0
fi
sleep 1.4
done
echo "algod not expected to be running but it is"
- if [ -n "$datadir" ]; then
+ if [ -n "$DATA_DIR" ]; then
echo "last 20 lines of node.log:"
- tail -20 "$datadir/node.log"
+ tail -20 "$DATA_DIR/node.log"
echo "================================"
echo "stdout and stdin:"
- cat "$datadir/algod-out.log"
+ cat "$DATA_DIR/algod-out.log"
echo "================================"
- cat "$datadir/algod-err.log"
+ cat "$DATA_DIR/algod-err.log"
fi
exit 1
}
function verify_one_running() {
# Starting up can take some time, so retry up to 2 seconds
- for TRIES in 1 2 3 4 5; do
+ for _ in 1 2 3 4 5; do
update_running_count
- if [ ${RUNNING_COUNT} -eq 1 ]; then
+ if [ "$RUNNING_COUNT" -eq 1 ]; then
return 0
fi
sleep .4
@@ -70,33 +75,33 @@ verify_none_running
#----------------------
# Test that we can start & stop a generic node with no overrides
echo Verifying a generic node will start using goal
-goal node start -d ${DATADIR}
+goal node start -d "$DATA_DIR"
verify_at_least_one_running
echo Verifying we can stop it using goal
-goal node stop -d ${DATADIR}
-verify_none_running ${DATADIR}
+goal node stop -d "$DATA_DIR"
+verify_none_running
#----------------------
# Test that we can start a generic node straight with no overrides
echo Verifying a generic node will start directly
-algod -d ${DATADIR} &
+algod -d "$DATA_DIR" &
verify_at_least_one_running
-pkill -u $(whoami) -x algod || true
-verify_none_running ${DATADIR}
+pkill -u "$(whoami)" -x algod || true
+verify_none_running
#----------------------
-# Test that we can start a generic node against the datadir
-# but that we cannot start a second one against same datadir
-echo Verifying that the datadir algod lock works correctly
-algod -d ${DATADIR} &
+# Test that we can start a generic node against the data dir
+# but that we cannot start a second one against same data dir
+echo Verifying that the data dir algod lock works correctly
+algod -d "$DATA_DIR" &
verify_at_least_one_running
-algod -d ${DATADIR} &
+algod -d "$DATA_DIR" &
verify_at_least_one_running # one should still be running
verify_one_running # in fact, exactly one should still be running
# clean up
-pkill -u $(whoami) -x algod || true
-verify_none_running ${DATADIR}
+pkill -u "$(whoami)" -x algod || true
+verify_none_running
echo "----------------------------------------------------------------------"
echo " DONE: e2e_basic_start_stop"
diff --git a/test/scripts/e2e_subs/e2e-app-simple.sh b/test/scripts/e2e_subs/e2e-app-simple.sh
index e1f1458ce..660487621 100755
--- a/test/scripts/e2e_subs/e2e-app-simple.sh
+++ b/test/scripts/e2e_subs/e2e-app-simple.sh
@@ -122,3 +122,27 @@ ${gcmd} app optin --app-id $APPID --from $ACCOUNT
# Succeed in clearing state for the app
${gcmd} app clear --app-id $APPID --from $ACCOUNT
+
+
+# Empty program:
+printf ' ' > "${TEMPDIR}/empty_clear.teal"
+
+# Fail to compile an empty program
+RES=$(${gcmd} clerk compile "${TEMPDIR}/empty_clear.teal" 2>&1 | tr -d '\n' || true)
+EXPERROR='Cannot assemble empty program text'
+if [[ $RES != *"${EXPERROR}"* ]]; then
+ echo RES="$RES"
+ echo EXPERROR="$EXPERROR"
+ date '+clerk-compile-test FAIL wrong error for compiling empty program %Y%m%d_%H%M%S'
+ false
+fi
+
+# Fail to create an app because the clear program is empty
+RES=$(${gcmd} app create --creator "${ACCOUNT}" --approval-prog "${TEMPDIR}/simple.teal" --clear-prog "${TEMPDIR}/empty_clear.teal" --global-byteslices 0 --global-ints 0 --local-byteslices 0 --local-ints 0 2>&1 | tr -d '\n' || true)
+EXPERROR='Cannot assemble empty program text'
+if [[ $RES != *"${EXPERROR}"* ]]; then
+ echo RES="$RES"
+ echo EXPERROR="$EXPERROR"
+ date '+app-create-test FAIL wrong error for creating app with empty clear program %Y%m%d_%H%M%S'
+ false
+fi
diff --git a/test/scripts/e2e_tx_latency.py b/test/scripts/e2e_tx_latency.py
new file mode 100644
index 000000000..b181002d9
--- /dev/null
+++ b/test/scripts/e2e_tx_latency.py
@@ -0,0 +1,400 @@
+#!/usr/bin/env python3
+#
+# Measure total percieved tx latency.
+# Submit transactions to algod, watch blocks for committed transaction.
+
+import argparse
+import atexit
+import base64
+import datetime
+import glob
+import json
+import logging
+import os
+import queue
+import re
+import shutil
+import statistics
+import subprocess
+import sys
+import tempfile
+import time
+import threading
+
+# pip install py-algorand-sdk
+import algosdk
+from algosdk.encoding import msgpack
+import algosdk.v2client
+import algosdk.v2client.algod
+
+logger = logging.getLogger(__name__)
+
+def openkmd(algodata):
+ kmdnetpath = sorted(glob.glob(os.path.join(algodata,'kmd-*','kmd.net')))[-1]
+ kmdnet = open(kmdnetpath, 'rt').read().strip()
+ kmdtokenpath = sorted(glob.glob(os.path.join(algodata,'kmd-*','kmd.token')))[-1]
+ kmdtoken = open(kmdtokenpath, 'rt').read().strip()
+ kmd = algosdk.kmd.KMDClient(kmdtoken, 'http://' + kmdnet)
+ return kmd
+
+def openalgod(algodata):
+ algodnetpath = os.path.join(algodata,'algod.net')
+ algodnet = open(algodnetpath, 'rt').read().strip()
+ algodtokenpath = os.path.join(algodata,'algod.token')
+ algodtoken = open(algodtokenpath, 'rt').read().strip()
+ algod = algosdk.v2client.algod.AlgodClient(algodtoken, 'http://' + algodnet)
+ return algod
+
+def addr_token_from_algod(algorand_data):
+ with open(os.path.join(algorand_data, 'algod.net')) as fin:
+ addr = fin.read().strip()
+ with open(os.path.join(algorand_data, 'algod.token')) as fin:
+ token = fin.read().strip()
+ if not addr.startswith('http'):
+ addr = 'http://' + addr
+ return addr, token
+
+def bstr(x):
+ if isinstance(x, bytes):
+ try:
+ return x.decode()
+ except:
+ pass
+ return x
+
+def obnice(ob):
+ if isinstance(ob, dict):
+ return {bstr(k):obnice(v) for k,v in ob.items()}
+ if isinstance(ob, list):
+ return [obnice(x) for x in ob]
+ return ob
+
+class TxLatencyTest:
+ def __init__(self, args, algorand_data=None, prev_round=None, out=None):
+ self.algorand_data = algorand_data
+ self.token = args.token
+ self.addr = args.addr
+ self.headers = header_list_to_dict(args.headers)
+ self.prev_round = prev_round
+ self.out = out or sys.stdout
+ self.lock = threading.Lock()
+ self.terminated = None
+ self._kmd = None
+ self._algod = None
+ self.privatekey = None
+ self.pubw = None
+ self.maxpubaddr = None
+ self.errors = []
+ self.statuses = []
+ self.jsonfile = None
+ self.sentq = queue.Queue()
+ self.txidq = queue.Queue()
+ self.period = 1/args.tps
+ self.sendcount = args.sendcount
+ self.go = True
+ self.roundTimes = {}
+ self.txTimes = []
+ return
+
+ def connect(self):
+ with self.lock:
+ self._connect()
+ return self._algod, self._kmd
+
+ def _connect(self):
+ if self._algod and self._kmd:
+ return
+
+ # should run from inside self.lock
+ algodata = self.algorand_data
+
+ logger.debug('pre kmd')
+ subprocess.run(['goal', 'kmd', 'start', '-t', '3600', '-d', algodata], timeout=5, check=True)
+ logger.debug('post kmd')
+ self._kmd = openkmd(algodata)
+ self._algod = self._algod_connect() #openalgod(algodata)
+
+ def algod(self):
+ with self.lock:
+ if self._algod is None:
+ self._algod = self._algod_connect()
+ return self._algod
+
+ def _algod_connect(self):
+ if self.algorand_data:
+ addr, token = addr_token_from_algod(self.algorand_data)
+ logger.debug('algod from %r, (%s %s)', self.algorand_data, addr, token)
+ else:
+ token = self.token
+ addr = self.addr
+ logger.debug('algod from args (%s %s)', self.addr, self.token)
+ self._algod = algosdk.v2client.algod.AlgodClient(token, addr, headers=self.headers)
+ return self._algod
+
+ def get_pub_wallet(self):
+ with self.lock:
+ self._connect()
+ if not (self.pubw and self.maxpubaddr):
+ # find private test node public wallet and its richest account
+ wallets = self._kmd.list_wallets()
+ pubwid = None
+ for xw in wallets:
+ if xw['name'] == 'unencrypted-default-wallet':
+ pubwid = xw['id']
+ pubw = self._kmd.init_wallet_handle(pubwid, '')
+ pubaddrs = self._kmd.list_keys(pubw)
+ maxamount = 0
+ maxpubaddr = None
+ for pa in pubaddrs:
+ pai = self._algod.account_info(pa)
+ if pai['amount'] > maxamount:
+ maxamount = pai['amount']
+ maxpubaddr = pai['address']
+ self.pubw = pubw
+ self.maxpubaddr = maxpubaddr
+ return self.pubw, self.maxpubaddr
+
+ def send_thread(self):
+ #opriv, opub = algosdk.account.generate_account()
+ algod = self.algod()
+ nextsend = time.time()
+ params = algod.suggested_params()
+ paramsMtime = nextsend
+ count = 0
+
+ while True:
+ txn = algosdk.transaction.PaymentTxn(self.maxpubaddr, 1000, params.first, params.last, params.gh, self.maxpubaddr, 1, gen=params.gen, flat_fee=True, note='{}_'.format(count).encode() + os.getrandom(8))
+ ptxid = txn.get_txid()
+ if self.privatekey:
+ stxn = txn.sign(self.privatekey)
+ else:
+ stxn = self._kmd.sign_transaction(self.pubw, '', txn)
+ txid = algod.send_transaction(stxn)
+ if ptxid != txid:
+ logger.error('python txid %s, API txid %s', ptxid, txid)
+ logger.debug('%r', txn.dictify())
+ sendt = time.time()
+ logger.debug('sent %s %f', txid, sendt)
+ self.sentq.put((txid, sendt))
+
+ if self.sendcount is not None:
+ self.sendcount -= 1
+ if self.sendcount <= 0:
+ # signal to consumer end of sending
+ self.sentq.put((None, None))
+ return
+
+ if sendt - paramsMtime > 5:
+ params = algod.suggested_params()
+ paramsMtime = sendt
+
+ while nextsend < sendt:
+ nextsend += self.period
+ time.sleep(nextsend - sendt)
+
+ def measure_thread(self):
+ lastround = self.prev_round
+ algod = self.algod()
+ while self.go:
+ b = self.nextblock(lastround)
+ if b is None:
+ print("got None nextblock. exiting")
+ return
+ b = msgpack.loads(b, strict_map_key=False, raw=True)
+ b = obnice(b)
+ nowround = b['block'].get('rnd', 0)
+ logger.debug('r%d', nowround)
+ if (lastround is not None) and (nowround != lastround + 1):
+ logger.info('round jump %d to %d', lastround, nowround)
+ self._block_handler(b)
+ lastround = nowround
+
+ def nextblock(self, lastround=None, retries=30):
+ trycount = 0
+ while (trycount < retries) and self.go:
+ trycount += 1
+ try:
+ return self._nextblock_inner(lastround)
+ except Exception as e:
+ if trycount >= retries:
+ logger.error('too many errors in nextblock retries')
+ raise
+ else:
+ logger.warning('error in nextblock(%r) (retrying): %s', lastround, e)
+ self._algod = None # retry with a new connection
+ time.sleep(1.2)
+ return None
+
+ def _nextblock_inner(self, lastround):
+ self.block_time = None
+ algod = self.algod()
+ if lastround is None:
+ status = algod.status()
+ lastround = status['last-round']
+ logger.debug('nextblock status last-round %s', lastround)
+ else:
+ try:
+ blk = algod.block_info(lastround + 1, response_format='msgpack')
+ if blk:
+ return blk
+ logger.warning('null block %d, lastround=%r', lastround+1, lastround)
+ except Exception as e:
+ pass
+ #logger.debug('could not get block %d: %s', lastround + 1, e, exc_info=True)
+ status = algod.status_after_block(lastround)
+ block_time = time.time() # the block has happened, don't count block data transit time
+ nbr = status['last-round']
+ retries = 30
+ while (nbr > lastround + 1) and self.go:
+ # if more than one block elapsed, we don't have a good time for either block
+ block_time = None
+ # try lastround+1 one last time
+ try:
+ blk = algod.block_info(lastround + 1, response_format='msgpack')
+ if blk:
+ return blk
+ logger.warning('null block %d, lastround=%r, status.last-round=%d', lastround+1, lastround, nbr)
+ time.sleep(1.1)
+ retries -= 1
+ if retries <= 0:
+ raise Exception("too many null block for %d", lastround+1)
+ except:
+ break
+ blk = algod.block_info(nbr, response_format='msgpack')
+ if blk:
+ self.block_time = block_time
+ return blk
+ raise Exception('got None for blk {}'.format(nbr))
+
+ def _block_handler(self, b):
+ block_time = self.block_time or time.time()
+ nowround = b['block'].get('rnd', 0)
+ self.roundTimes[nowround] = block_time
+ # throw away txns, count is kept in round differential ['block']['tc']
+ stxibs = b['block'].get('txns', [])
+ txids = []
+ for stxib in stxibs:
+ txn = stxib['txn']
+ #logger.debug('stxib.txn %r', txn)
+ hgi = stxib.pop('hgi', False)
+ if hgi:
+ txn['gh'] = b['block']['gh']
+ txn['gen'] = bstr(b['block']['gen'])
+ txnd = txn
+ txn = algosdk.transaction.Transaction.undictify(txn)
+ txid = txn.get_txid()
+ logger.debug('rx txn %r, txid %s', txnd, txid)
+ txids.append(txid)
+ self.txidq.put((txids, block_time))
+
+ def join_thread(self):
+ lastSendt = None
+ sentq = self.sentq
+ sentByTxid = {}
+ while self.go:
+ if sentq is not None:
+ try:
+ txid, sendt = self.sentq.get(block=True, timeout=0.2)
+ if sendt is None:
+ sentq = None
+ else:
+ lastSendt = sendt
+ sentByTxid[txid] = sendt
+ except queue.Empty:
+ pass
+ elif (lastSendt is None) or (time.time() > (lastSendt + 8)):
+ self.go = False
+ return
+ try:
+ txids, rxt = self.txidq.get(block=False)
+ for txid in txids:
+ sendt = sentByTxid.get(txid)
+ if sendt is not None:
+ dt = rxt - sendt
+ self.txTimes.append(dt)
+ logger.debug('rx %s %f', txid, dt)
+ self.out.write('{}\n'.format(dt))
+ else:
+ logger.debug('unk blk txid %r', txid)
+ except queue.Empty:
+ pass
+
+def header_list_to_dict(hlist):
+ if not hlist:
+ return None
+ p = re.compile(r':\s+')
+ out = {}
+ for x in hlist:
+ a, b = p.split(x, 1)
+ out[a] = b
+ return out
+
+def main():
+ ap = argparse.ArgumentParser()
+ ap.add_argument('-d', '--algod', default=None, help='algod data dir')
+ ap.add_argument('-a', '--addr', default=None, help='algod host:port address')
+ ap.add_argument('-t', '--token', default=None, help='algod API access token')
+ ap.add_argument('--header', dest='headers', nargs='*', help='"Name: value" HTTP header (repeatable)')
+ ap.add_argument('--tps', type=float, default=5, help='TPS to send at')
+ ap.add_argument('--sendcount', type=int, default=50, help='number of test txns to send at 5 TPS')
+ ap.add_argument('--verbose', default=False, action='store_true')
+ args = ap.parse_args()
+
+ if args.verbose:
+ logging.basicConfig(level=logging.DEBUG)
+ else:
+ logging.basicConfig(level=logging.INFO)
+
+ algorand_data = args.algod or os.getenv('ALGORAND_DATA')
+ if not algorand_data and not ((args.token or args.headers) and args.addr):
+ sys.stderr.write('must specify algod data dir by $ALGORAND_DATA or -d/--algod; OR --a/--addr and -t/--token\n')
+ sys.exit(1)
+
+ out = sys.stdout
+
+ bot = TxLatencyTest(
+ args,
+ algorand_data,
+ prev_round=None,
+ out=out,
+ )
+
+ pubw, maxpubaddr = bot.get_pub_wallet()
+ logger.debug('get pub wallet -> %s %s', pubw, maxpubaddr)
+
+ sender = threading.Thread(target=bot.send_thread)
+ sender.start()
+ measure = threading.Thread(target=bot.measure_thread)
+ measure.start()
+ merge = threading.Thread(target=bot.join_thread)
+ merge.start()
+ sender.join()
+ measure.join()
+ merge.join()
+
+ txTimes = bot.txTimes
+ rounds = sorted(bot.roundTimes.keys())
+ prevRound = None
+ prevRoundTime = None
+ roundDts = []
+ for rnd in rounds:
+ rndTime = bot.roundTimes[rnd]
+ if (prevRound is not None) and (prevRound + 1 == rnd):
+ dt = rndTime - prevRoundTime
+ roundDts.append(dt)
+ prevRound = rnd
+ prevRoundTime = rndTime
+ roundTimeMean = statistics.mean(roundDts)
+ roundTimeMin = min(roundDts)
+ roundTimeMax = max(roundDts)
+ txMean = statistics.mean(txTimes)
+ tmin = min(txTimes)
+ tmax = max(txTimes)
+ out.write('# {} txns measured, {} rounds seen\n'.format(len(txTimes), len(rounds)))
+ out.write('# rnd (min={}, mean={}, max={})\n'.format(roundTimeMin, roundTimeMean, roundTimeMax))
+ out.write('# tx (min={}, mean={}, max={})\n'.format(tmin, txMean, tmax))
+ out.write('# tx (min={}, mean={}, max={}) (/(mean rnd))\n'.format(tmin/roundTimeMean, txMean/roundTimeMean, tmax/roundTimeMean))
+ return 0
+
+if __name__ == '__main__':
+ main()
diff --git a/test/scripts/latency_plot.py b/test/scripts/latency_plot.py
new file mode 100644
index 000000000..aab94d439
--- /dev/null
+++ b/test/scripts/latency_plot.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+#
+# process pingpong TotalLatencyOut output file into a graph
+#
+# requires:
+# pip install matplotlib
+
+import argparse
+import gzip
+import math
+import os
+import statistics
+import sys
+import tarfile
+
+def mmstdm(data):
+ dmin = min(data)
+ dmax = max(data)
+ dmean = statistics.mean(data)
+ dstd = statistics.pstdev(data)
+ return f'[{dmin:.3f}/{dmean:.3f} ({dstd:.3f})/{dmax:.3f}]'
+
+class LatencyAnalyzer:
+ def __init__(self):
+ self.data = []
+ def read(self, path):
+ if path.endswith('.gz'):
+ with gzip.open(path, 'rt') as fin:
+ self.rlines(fin)
+ else:
+ with open(path, 'rt') as fin:
+ self.rlines(fin)
+ def rlines(self, linesource):
+ for line in linesource:
+ rec = int(line.strip())/1000000000.0
+ self.data.append(rec)
+ def plot(self, outname):
+ from matplotlib import pyplot as plt
+ plt.plot(self.data)
+ plt.savefig(outname + '.png', format='png')
+ plt.savefig(outname + '.svg', format='svg')
+ def report_data(self):
+ self.data.sort()
+ some = int(math.log(len(self.data))*4)
+ lowest = self.data[:some]
+ highest = self.data[-some:]
+ return some, lowest, highest
+ def report(self):
+ lines = []
+ some, lowest, highest = self.report_data()
+ lines.append(f'{len(self.data)} points: {mmstdm(self.data)}')
+ lines.append('min {:.3f}s'.format(min(self.data)))
+ lines.append('max {:.3f}s'.format(max(self.data)))
+ lines.append(f'lowest-{some}: {mmstdm(lowest)}')
+ lines.append(f'highest-{some}: {mmstdm(highest)}')
+ return '\n'.join(lines)
+ def report_html(self):
+ lines = []
+ some, lowest, highest = self.report_data()
+ lines.append('<h2>Latency</h2>')
+ lines.append('<div class="legend">[min/mean (std)/max]</div>')
+ lines.append(f'<div>{len(self.data)} points: {mmstdm(self.data)}</div>')
+ lines.append(f'<div>min {min(self.data):.3f}s</div>')
+ lines.append(f'<div>max {max(self.data):.3f}s</div>')
+ lines.append(f'<div>lowest-{some}: {mmstdm(lowest)}</div>')
+ lines.append(f'<div>highest-{some}: {mmstdm(highest)}</div>')
+ return ''.join(lines)
+
+def main():
+ ap = argparse.ArgumentParser()
+ ap.add_argument('latency_log', nargs='*')
+ ap.add_argument('-p', '--plot', help='plot base name for .png .svg')
+ ap.add_argument('-a', '--aout', help='report text output path (append)')
+ ap.add_argument('-H', '--ahtml', help='report html output path (append)')
+ ap.add_argument('--tardir')
+ args = ap.parse_args()
+
+ la = LatencyAnalyzer()
+ for path in args.latency_log:
+ la.read(path)
+ if args.tardir:
+ for dirpath, dirnames, filenames in os.walk(args.tardir):
+ for fname in filenames:
+ if fname.endswith('.tar.bz2'):
+ tarname = os.path.join(dirpath, fname)
+ tf = tarfile.open(tarname, 'r:bz2')
+ for tinfo in tf:
+ if not tinfo.isfile():
+ continue
+ if 'latency' in tinfo.name:
+ rawf = tf.extractfile(tinfo)
+ if tinfo.name.endswith('.gz'):
+ fin = gzip.open(rawf, 'rt')
+ else:
+ fin = rawf
+ rawf = None
+ try:
+ la.rlines(fin)
+ except Exception as e:
+ sys.stderr.write(f'{tarname}/{tinfo.name}: {e}\n')
+ fin.close()
+ if rawf is not None:
+ rawf.close()
+
+ if args.plot:
+ la.plot(args.plot)
+ if args.ahtml:
+ with open(args.ahtml, 'at') as fout:
+ fout.write(la.report_html())
+ if args.aout:
+ with open(args.aout, 'at') as fout:
+ fout.write(la.report())
+ if (not args.aout) and (not args.ahtml):
+ print(la.report())
+
+if __name__ == '__main__':
+ main()
diff --git a/test/testdata/configs/config-v26.json b/test/testdata/configs/config-v26.json
index 31d693f90..6589d0326 100644
--- a/test/testdata/configs/config-v26.json
+++ b/test/testdata/configs/config-v26.json
@@ -38,6 +38,7 @@
"EnableBlockServiceFallbackToArchiver": true,
"EnableCatchupFromArchiveServers": false,
"EnableDeveloperAPI": false,
+ "EnableExperimentalAPI": false,
"EnableGossipBlockService": true,
"EnableIncomingMessageFilter": false,
"EnableLedgerService": false,
@@ -93,9 +94,9 @@
"RunHosted": false,
"SuggestedFeeBlockHistory": 3,
"SuggestedFeeSlidingWindowSize": 50,
+ "TelemetryToLog": true,
"TLSCertFile": "",
"TLSKeyFile": "",
- "TelemetryToLog": true,
"TransactionSyncDataExchangeRate": 0,
"TransactionSyncSignificantMessageThreshold": 0,
"TxIncomingFilteringFlags": 1,
diff --git a/test/testdata/configs/config-v27.json b/test/testdata/configs/config-v27.json
new file mode 100644
index 000000000..137578e0f
--- /dev/null
+++ b/test/testdata/configs/config-v27.json
@@ -0,0 +1,116 @@
+{
+ "Version": 27,
+ "AccountUpdatesStatsInterval": 5000000000,
+ "AccountsRebuildSynchronousMode": 1,
+ "AgreementIncomingBundlesQueueLength": 15,
+ "AgreementIncomingProposalsQueueLength": 50,
+ "AgreementIncomingVotesQueueLength": 20000,
+ "AnnounceParticipationKey": true,
+ "Archival": false,
+ "BaseLoggerDebugLevel": 4,
+ "BlockServiceCustomFallbackEndpoints": "",
+ "BroadcastConnectionsLimit": -1,
+ "CadaverDirectory": "",
+ "CadaverSizeTarget": 0,
+ "CatchpointFileHistoryLength": 365,
+ "CatchpointInterval": 10000,
+ "CatchpointTracking": 0,
+ "CatchupBlockDownloadRetryAttempts": 1000,
+ "CatchupBlockValidateMode": 0,
+ "CatchupFailurePeerRefreshRate": 10,
+ "CatchupGossipBlockFetchTimeoutSec": 4,
+ "CatchupHTTPBlockFetchTimeoutSec": 4,
+ "CatchupLedgerDownloadRetryAttempts": 50,
+ "CatchupParallelBlocks": 16,
+ "ConnectionsRateLimitingCount": 60,
+ "ConnectionsRateLimitingWindowSeconds": 1,
+ "DNSBootstrapID": "<network>.algorand.network",
+ "DNSSecurityFlags": 1,
+ "DeadlockDetection": 0,
+ "DeadlockDetectionThreshold": 30,
+ "DisableLocalhostConnectionRateLimit": true,
+ "DisableNetworking": false,
+ "DisableOutgoingConnectionThrottling": false,
+ "EnableAccountUpdatesStats": false,
+ "EnableAgreementReporting": false,
+ "EnableAgreementTimeMetrics": false,
+ "EnableAssembleStats": false,
+ "EnableBlockService": false,
+ "EnableBlockServiceFallbackToArchiver": true,
+ "EnableCatchupFromArchiveServers": false,
+ "EnableDeveloperAPI": false,
+ "EnableExperimentalAPI": false,
+ "EnableGossipBlockService": true,
+ "EnableIncomingMessageFilter": false,
+ "EnableLedgerService": false,
+ "EnableMetricReporting": false,
+ "EnableOutgoingNetworkMessageFiltering": true,
+ "EnablePingHandler": true,
+ "EnableProcessBlockStats": false,
+ "EnableProfiler": false,
+ "EnableRequestLogger": false,
+ "EnableRuntimeMetrics": false,
+ "EnableTopAccountsReporting": false,
+ "EnableTxBacklogRateLimiting": false,
+ "EnableUsageLog": false,
+ "EnableVerbosedTransactionSyncLogging": false,
+ "EndpointAddress": "127.0.0.1:0",
+ "FallbackDNSResolverAddress": "",
+ "ForceFetchTransactions": false,
+ "ForceRelayMessages": false,
+ "GossipFanout": 4,
+ "HeartbeatUpdateInterval": 600,
+ "IncomingConnectionsLimit": 2400,
+ "IncomingMessageFilterBucketCount": 5,
+ "IncomingMessageFilterBucketSize": 512,
+ "IsIndexerActive": false,
+ "LedgerSynchronousMode": 2,
+ "LogArchiveMaxAge": "",
+ "LogArchiveName": "node.archive.log",
+ "LogSizeLimit": 1073741824,
+ "MaxAPIBoxPerApplication": 100000,
+ "MaxAPIResourcesPerAccount": 100000,
+ "MaxAcctLookback": 4,
+ "MaxCatchpointDownloadDuration": 7200000000000,
+ "MaxConnectionsPerIP": 15,
+ "MinCatchpointFileDownloadBytesPerSecond": 20480,
+ "NetAddress": "",
+ "NetworkMessageTraceServer": "",
+ "NetworkProtocolVersion": "",
+ "NodeExporterListenAddress": ":9100",
+ "NodeExporterPath": "./node_exporter",
+ "OptimizeAccountsDatabaseOnStartup": false,
+ "OutgoingMessageFilterBucketCount": 3,
+ "OutgoingMessageFilterBucketSize": 128,
+ "ParticipationKeysRefreshInterval": 60000000000,
+ "PeerConnectionsUpdateInterval": 3600,
+ "PeerPingPeriodSeconds": 0,
+ "PriorityPeers": {},
+ "ProposalAssemblyTime": 500000000,
+ "PublicAddress": "",
+ "ReconnectTime": 60000000000,
+ "ReservedFDs": 256,
+ "RestConnectionsHardLimit": 2048,
+ "RestConnectionsSoftLimit": 1024,
+ "RestReadTimeoutSeconds": 15,
+ "RestWriteTimeoutSeconds": 120,
+ "RunHosted": false,
+ "SuggestedFeeBlockHistory": 3,
+ "SuggestedFeeSlidingWindowSize": 50,
+ "TLSCertFile": "",
+ "TLSKeyFile": "",
+ "TelemetryToLog": true,
+ "TransactionSyncDataExchangeRate": 0,
+ "TransactionSyncSignificantMessageThreshold": 0,
+ "TxBacklogReservedCapacityPerPeer": 20,
+ "TxBacklogServiceRateWindowSeconds": 10,
+ "TxBacklogSize": 26000,
+ "TxIncomingFilteringFlags": 1,
+ "TxPoolExponentialIncreaseFactor": 2,
+ "TxPoolSize": 75000,
+ "TxSyncIntervalSeconds": 60,
+ "TxSyncServeResponseSize": 1000000,
+ "TxSyncTimeoutSeconds": 30,
+ "UseXForwardedForAddressField": "",
+ "VerifiedTranscationsCacheSize": 150000
+}
diff --git a/tools/boxkey/convertBoxKey.go b/tools/boxkey/convertBoxKey.go
new file mode 100644
index 000000000..0d77d2f84
--- /dev/null
+++ b/tools/boxkey/convertBoxKey.go
@@ -0,0 +1,48 @@
+// 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 main
+
+import (
+ "encoding/base64"
+ "encoding/hex"
+ "flag"
+ "fmt"
+
+ "github.com/algorand/avm-abi/apps"
+)
+
+func main() {
+ var name string
+ var appIdx uint64
+ flag.Uint64Var(&appIdx, "a", 0, "base64/algorand address to convert to the other")
+ flag.StringVar(&name, "n", "", "base64 box name")
+ flag.Parse()
+
+ if appIdx == 0 && name == "" {
+ fmt.Println("provide input with '-a' and '-k' flags.")
+ return
+ }
+
+ nameBytes, err := base64.StdEncoding.DecodeString(name)
+ if err != nil {
+ fmt.Println("invalid key value")
+ return
+ }
+ key := apps.MakeBoxKey(appIdx, string(nameBytes))
+ fmt.Println(base64.StdEncoding.EncodeToString([]byte(key)))
+ fmt.Println(hex.EncodeToString([]byte(key)))
+}
diff --git a/tools/debug/algodump/main.go b/tools/debug/algodump/main.go
index f61944ff1..be80768fc 100644
--- a/tools/debug/algodump/main.go
+++ b/tools/debug/algodump/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/debug/carpenter/main.go b/tools/debug/carpenter/main.go
index d43f2267b..625211694 100644
--- a/tools/debug/carpenter/main.go
+++ b/tools/debug/carpenter/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/debug/coroner/main.go b/tools/debug/coroner/main.go
index 669e560de..1ea04537d 100644
--- a/tools/debug/coroner/main.go
+++ b/tools/debug/coroner/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/debug/determaccount/main.go b/tools/debug/determaccount/main.go
index 84dfbf73f..2c3c16d65 100644
--- a/tools/debug/determaccount/main.go
+++ b/tools/debug/determaccount/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/debug/dumpblocks/main.go b/tools/debug/dumpblocks/main.go
index 09698bdb0..461e7949c 100644
--- a/tools/debug/dumpblocks/main.go
+++ b/tools/debug/dumpblocks/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/debug/genconsensusconfig/main.go b/tools/debug/genconsensusconfig/main.go
index 74f6eda2f..edef949d3 100644
--- a/tools/debug/genconsensusconfig/main.go
+++ b/tools/debug/genconsensusconfig/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/debug/logfilter/main.go b/tools/debug/logfilter/main.go
index 1c7545a48..11878ea47 100644
--- a/tools/debug/logfilter/main.go
+++ b/tools/debug/logfilter/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/debug/logfilter/main_test.go b/tools/debug/logfilter/main_test.go
index 45ab8605f..6b6902d62 100644
--- a/tools/debug/logfilter/main_test.go
+++ b/tools/debug/logfilter/main_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/misc/convertAddress.go b/tools/misc/convertAddress.go
index c8c4e9997..801c26106 100644
--- a/tools/misc/convertAddress.go
+++ b/tools/misc/convertAddress.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/bootstrap.go b/tools/network/bootstrap.go
index cd15ef2ab..b3bd79b23 100644
--- a/tools/network/bootstrap.go
+++ b/tools/network/bootstrap.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/cloudflare/cloudflare.go b/tools/network/cloudflare/cloudflare.go
index 714fb9635..84f62f653 100644
--- a/tools/network/cloudflare/cloudflare.go
+++ b/tools/network/cloudflare/cloudflare.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/cloudflare/createRecord.go b/tools/network/cloudflare/createRecord.go
index c68747f5b..a41dcbccc 100644
--- a/tools/network/cloudflare/createRecord.go
+++ b/tools/network/cloudflare/createRecord.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/cloudflare/deleteRecord.go b/tools/network/cloudflare/deleteRecord.go
index f0bf90ce5..864a223df 100644
--- a/tools/network/cloudflare/deleteRecord.go
+++ b/tools/network/cloudflare/deleteRecord.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/cloudflare/helpers.go b/tools/network/cloudflare/helpers.go
index e4b995f67..e330d9330 100644
--- a/tools/network/cloudflare/helpers.go
+++ b/tools/network/cloudflare/helpers.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/cloudflare/listRecords.go b/tools/network/cloudflare/listRecords.go
index 1617b6118..34efc71d5 100644
--- a/tools/network/cloudflare/listRecords.go
+++ b/tools/network/cloudflare/listRecords.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/cloudflare/updateRecord.go b/tools/network/cloudflare/updateRecord.go
index c4be7c362..7add2d05b 100644
--- a/tools/network/cloudflare/updateRecord.go
+++ b/tools/network/cloudflare/updateRecord.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/cloudflare/zones.go b/tools/network/cloudflare/zones.go
index f5aa4b9ac..c9a54a6df 100644
--- a/tools/network/cloudflare/zones.go
+++ b/tools/network/cloudflare/zones.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/anchor.go b/tools/network/dnssec/anchor.go
index 8f7066904..18d259aca 100644
--- a/tools/network/dnssec/anchor.go
+++ b/tools/network/dnssec/anchor.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/anchor_test.go b/tools/network/dnssec/anchor_test.go
index cea0d2adb..dbd7396e0 100644
--- a/tools/network/dnssec/anchor_test.go
+++ b/tools/network/dnssec/anchor_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/client.go b/tools/network/dnssec/client.go
index 5ebfc8478..79cd79a9c 100644
--- a/tools/network/dnssec/client.go
+++ b/tools/network/dnssec/client.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/client_test.go b/tools/network/dnssec/client_test.go
index 3bcc8f79d..7c2eede7d 100644
--- a/tools/network/dnssec/client_test.go
+++ b/tools/network/dnssec/client_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/config.go b/tools/network/dnssec/config.go
index a9c38bff9..a5c9bbbfe 100644
--- a/tools/network/dnssec/config.go
+++ b/tools/network/dnssec/config.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/config_test.go b/tools/network/dnssec/config_test.go
index 6115c7eef..8ad1ae511 100644
--- a/tools/network/dnssec/config_test.go
+++ b/tools/network/dnssec/config_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/config_unix.go b/tools/network/dnssec/config_unix.go
index 4a8c574de..74b3cb0f9 100644
--- a/tools/network/dnssec/config_unix.go
+++ b/tools/network/dnssec/config_unix.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/config_unix_test.go b/tools/network/dnssec/config_unix_test.go
index c757b5369..07a6a584b 100644
--- a/tools/network/dnssec/config_unix_test.go
+++ b/tools/network/dnssec/config_unix_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/config_windows.go b/tools/network/dnssec/config_windows.go
index 41d695007..8d291be67 100644
--- a/tools/network/dnssec/config_windows.go
+++ b/tools/network/dnssec/config_windows.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/dialer.go b/tools/network/dnssec/dialer.go
index 8bb9f869b..08144ae5b 100644
--- a/tools/network/dnssec/dialer.go
+++ b/tools/network/dnssec/dialer.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/dnssec_test.go b/tools/network/dnssec/dnssec_test.go
index 496499048..86c723918 100644
--- a/tools/network/dnssec/dnssec_test.go
+++ b/tools/network/dnssec/dnssec_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/relay-check/main.go b/tools/network/dnssec/relay-check/main.go
index 4b2dcad29..f82211e09 100644
--- a/tools/network/dnssec/relay-check/main.go
+++ b/tools/network/dnssec/relay-check/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/resolver.go b/tools/network/dnssec/resolver.go
index 5aabcfd28..2b0b873c3 100644
--- a/tools/network/dnssec/resolver.go
+++ b/tools/network/dnssec/resolver.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/sort.go b/tools/network/dnssec/sort.go
index 0bdf17f88..2c8de2653 100644
--- a/tools/network/dnssec/sort.go
+++ b/tools/network/dnssec/sort.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/sort_test.go b/tools/network/dnssec/sort_test.go
index 3440e2303..ab1be21fb 100644
--- a/tools/network/dnssec/sort_test.go
+++ b/tools/network/dnssec/sort_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/testHarness.go b/tools/network/dnssec/testHarness.go
index 5f5310a8e..0f4c7f416 100644
--- a/tools/network/dnssec/testHarness.go
+++ b/tools/network/dnssec/testHarness.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/trustchain.go b/tools/network/dnssec/trustchain.go
index 3cde9bea7..15f69aea1 100644
--- a/tools/network/dnssec/trustchain.go
+++ b/tools/network/dnssec/trustchain.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/trustedchain_test.go b/tools/network/dnssec/trustedchain_test.go
index 2f671c007..6310e22ab 100644
--- a/tools/network/dnssec/trustedchain_test.go
+++ b/tools/network/dnssec/trustedchain_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/trustedzone.go b/tools/network/dnssec/trustedzone.go
index b3f20dc44..a71385503 100644
--- a/tools/network/dnssec/trustedzone.go
+++ b/tools/network/dnssec/trustedzone.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/trustedzone_test.go b/tools/network/dnssec/trustedzone_test.go
index 6d3bbaa04..877a68741 100644
--- a/tools/network/dnssec/trustedzone_test.go
+++ b/tools/network/dnssec/trustedzone_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/util.go b/tools/network/dnssec/util.go
index 1622f490f..48f5c9448 100644
--- a/tools/network/dnssec/util.go
+++ b/tools/network/dnssec/util.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/dnssec/util_test.go b/tools/network/dnssec/util_test.go
index d467d52c8..2a77ad1c0 100644
--- a/tools/network/dnssec/util_test.go
+++ b/tools/network/dnssec/util_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/externalIP.go b/tools/network/externalIP.go
index a726d03fc..121c43df3 100644
--- a/tools/network/externalIP.go
+++ b/tools/network/externalIP.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/resolveController.go b/tools/network/resolveController.go
index fd12d0dbf..a36914f21 100644
--- a/tools/network/resolveController.go
+++ b/tools/network/resolveController.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/resolveController_test.go b/tools/network/resolveController_test.go
index 8a843c902..db8a80573 100644
--- a/tools/network/resolveController_test.go
+++ b/tools/network/resolveController_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/resolver.go b/tools/network/resolver.go
index 467bb1e12..b823b17b2 100644
--- a/tools/network/resolver.go
+++ b/tools/network/resolver.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/resolver_test.go b/tools/network/resolver_test.go
index a00f07ea3..6efe1bea4 100644
--- a/tools/network/resolver_test.go
+++ b/tools/network/resolver_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -22,14 +22,16 @@ import (
"testing"
"time"
- "github.com/algorand/go-algorand/test/partitiontest"
"github.com/stretchr/testify/require"
+
+ "github.com/algorand/go-algorand/test/partitiontest"
)
-func TestResolver(t *testing.T) {
+func TestResolverWithDefaultDNSResolution(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
- // start with a resolver that has no specific DNS address defined.
+ // configure a resolver that has no specific DNS address defined.
// we want to make sure that it will go to the default DNS server ( 8.8.8.8 )
resolver := Resolver{}
cname, addrs, err := resolver.LookupSRV(context.Background(), "telemetry", "tls", "devnet.algodev.network")
@@ -37,18 +39,34 @@ func TestResolver(t *testing.T) {
require.Equal(t, "_telemetry._tls.devnet.algodev.network.", cname)
require.True(t, len(addrs) == 1)
require.Equal(t, defaultDNSAddress, resolver.EffectiveResolverDNS())
+}
- // specify a specific resolver to work with ( cloudflare DNS server is 1.1.1.1 )
- cloudFlareIPAddr, _ := net.ResolveIPAddr("ip", "1.1.1.1")
+func TestResolverWithCloudflareDNSResolution(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ resolver := Resolver{}
+
+ // The test previously specified Cloudflare's primary DNS server (1.1.1.1).
+ // However, CircleCI began blocking requests to 1.1.1.1. In order to
+ // preserve the test's spirit, it now uses Cloudflare's secondary DNS
+ // server (1.0.0.1).
+ cloudflareIPAddr, _ := net.ResolveIPAddr("ip", "1.0.0.1")
resolver = Resolver{
- dnsAddress: *cloudFlareIPAddr,
+ dnsAddress: *cloudflareIPAddr,
}
- cname, addrs, err = resolver.LookupSRV(context.Background(), "telemetry", "tls", "devnet.algodev.network")
+ cname, addrs, err := resolver.LookupSRV(context.Background(), "telemetry", "tls", "devnet.algodev.network")
require.NoError(t, err)
require.Equal(t, "_telemetry._tls.devnet.algodev.network.", cname)
require.True(t, len(addrs) == 1)
- require.Equal(t, "1.1.1.1", resolver.EffectiveResolverDNS())
+ require.Equal(t, "1.0.0.1", resolver.EffectiveResolverDNS())
+}
+func TestResolverWithInvalidDNSResolution(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ resolver := Resolver{}
// specify an invalid dns resolver ip address and examine the fail case.
dummyIPAddr, _ := net.ResolveIPAddr("ip", "255.255.128.1")
resolver = Resolver{
@@ -56,7 +74,7 @@ func TestResolver(t *testing.T) {
}
timingOutContext, timingOutContextFunc := context.WithTimeout(context.Background(), time.Duration(100)*time.Millisecond)
defer timingOutContextFunc()
- cname, addrs, err = resolver.LookupSRV(timingOutContext, "telemetry", "tls", "devnet.algodev.network")
+ cname, addrs, err := resolver.LookupSRV(timingOutContext, "telemetry", "tls", "devnet.algodev.network")
require.Error(t, err)
require.Equal(t, "", cname)
require.True(t, len(addrs) == 0)
diff --git a/tools/network/telemetryURIUpdateService.go b/tools/network/telemetryURIUpdateService.go
index ab0cc8110..becae7c74 100644
--- a/tools/network/telemetryURIUpdateService.go
+++ b/tools/network/telemetryURIUpdateService.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/network/telemetryURIUpdateService_test.go b/tools/network/telemetryURIUpdateService_test.go
index afe8bf242..1014d28df 100644
--- a/tools/network/telemetryURIUpdateService_test.go
+++ b/tools/network/telemetryURIUpdateService_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/teal/algotmpl/extract.go b/tools/teal/algotmpl/extract.go
index b8c045366..f542d6bce 100644
--- a/tools/teal/algotmpl/extract.go
+++ b/tools/teal/algotmpl/extract.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/teal/algotmpl/main.go b/tools/teal/algotmpl/main.go
index 7568dda92..824ab7ee9 100644
--- a/tools/teal/algotmpl/main.go
+++ b/tools/teal/algotmpl/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/teal/dkey/dsign/main.go b/tools/teal/dkey/dsign/main.go
index 6f380d8a0..5a4e2adfc 100644
--- a/tools/teal/dkey/dsign/main.go
+++ b/tools/teal/dkey/dsign/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/tools/teal/tealcut/main.go b/tools/teal/tealcut/main.go
index 7615cdc97..0740929db 100644
--- a/tools/teal/tealcut/main.go
+++ b/tools/teal/tealcut/main.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/cmdUtils.go b/util/cmdUtils.go
index 29641e628..1382be178 100644
--- a/util/cmdUtils.go
+++ b/util/cmdUtils.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/codecs/json.go b/util/codecs/json.go
index 2d2c21134..e283ef062 100644
--- a/util/codecs/json.go
+++ b/util/codecs/json.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/codecs/json_test.go b/util/codecs/json_test.go
index de35acf3c..1f5653197 100644
--- a/util/codecs/json_test.go
+++ b/util/codecs/json_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/condvar/timedwait.go b/util/condvar/timedwait.go
index a4605d099..e14f2b33b 100644
--- a/util/condvar/timedwait.go
+++ b/util/condvar/timedwait.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/condvar/timedwait_test.go b/util/condvar/timedwait_test.go
index 20b8f70a4..fa7deef21 100644
--- a/util/condvar/timedwait_test.go
+++ b/util/condvar/timedwait_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/dbpair.go b/util/db/dbpair.go
index 95be09263..0fa382063 100644
--- a/util/db/dbpair.go
+++ b/util/db/dbpair.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/dbutil.go b/util/db/dbutil.go
index 34a32320a..7e62dfee6 100644
--- a/util/db/dbutil.go
+++ b/util/db/dbutil.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/dbutil_test.go b/util/db/dbutil_test.go
index 412f04a6c..c35beccb2 100644
--- a/util/db/dbutil_test.go
+++ b/util/db/dbutil_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/fullfsync_darwin.go b/util/db/fullfsync_darwin.go
index 8a529305a..3261e7864 100644
--- a/util/db/fullfsync_darwin.go
+++ b/util/db/fullfsync_darwin.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/initialize.go b/util/db/initialize.go
index 36bf83770..fc11bc1aa 100644
--- a/util/db/initialize.go
+++ b/util/db/initialize.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/initialize_test.go b/util/db/initialize_test.go
index d7b879d6a..c0c8774b1 100644
--- a/util/db/initialize_test.go
+++ b/util/db/initialize_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/interfaces.go b/util/db/interfaces.go
index 5d4abed85..938edc103 100644
--- a/util/db/interfaces.go
+++ b/util/db/interfaces.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/perf_test.go b/util/db/perf_test.go
index 1e0a8bccd..5740ce2d4 100644
--- a/util/db/perf_test.go
+++ b/util/db/perf_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/versioning.go b/util/db/versioning.go
index 28f6b1dbd..6104fe6b9 100644
--- a/util/db/versioning.go
+++ b/util/db/versioning.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/db/versioning_test.go b/util/db/versioning_test.go
index fd2fecc23..74f048e30 100644
--- a/util/db/versioning_test.go
+++ b/util/db/versioning_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/execpool/backlog.go b/util/execpool/backlog.go
index 966aafe97..9e95ebe65 100644
--- a/util/execpool/backlog.go
+++ b/util/execpool/backlog.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -43,6 +43,7 @@ type backlogItemTask struct {
type BacklogPool interface {
ExecutionPool
EnqueueBacklog(enqueueCtx context.Context, t ExecFunc, arg interface{}, out chan interface{}) error
+ BufferSize() (length, capacity int)
}
// MakeBacklog creates a backlog
@@ -94,6 +95,11 @@ func (b *backlog) Enqueue(enqueueCtx context.Context, t ExecFunc, arg interface{
}
}
+// BufferSize returns the length and the capacity of the buffer
+func (b *backlog) BufferSize() (length, capacity int) {
+ return len(b.buffer), cap(b.buffer)
+}
+
// Enqueue enqueues a single task into the backlog
func (b *backlog) EnqueueBacklog(enqueueCtx context.Context, t ExecFunc, arg interface{}, out chan interface{}) error {
select {
diff --git a/util/execpool/pool.go b/util/execpool/pool.go
index 1b37dd95f..50acbf9a5 100644
--- a/util/execpool/pool.go
+++ b/util/execpool/pool.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/io.go b/util/io.go
index 43068f39c..1ab09b3b6 100644
--- a/util/io.go
+++ b/util/io.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/io_test.go b/util/io_test.go
index 353616c64..591905113 100644
--- a/util/io_test.go
+++ b/util/io_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/counter.go b/util/metrics/counter.go
index 5c195b5f8..bb8355cac 100644
--- a/util/metrics/counter.go
+++ b/util/metrics/counter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -66,13 +66,12 @@ func (counter *Counter) Inc(labels map[string]string) {
if len(labels) == 0 {
counter.fastAddUint64(1)
} else {
- counter.Add(1.0, labels)
+ counter.addLabels(1.0, labels)
}
}
-// Add increases counter by x
-// For adding an integer, see AddUint64(x)
-func (counter *Counter) Add(x float64, labels map[string]string) {
+// addLabels increases counter by x
+func (counter *Counter) addLabels(x uint64, labels map[string]string) {
counter.Lock()
defer counter.Unlock()
@@ -95,13 +94,12 @@ func (counter *Counter) Add(x float64, labels map[string]string) {
}
// AddUint64 increases counter by x
-// If labels is nil this is much faster than Add()
-// Calls through to Add() if labels is not nil.
+// If labels is nil this is much faster than if labels is not nil.
func (counter *Counter) AddUint64(x uint64, labels map[string]string) {
if len(labels) == 0 {
counter.fastAddUint64(x)
} else {
- counter.Add(float64(x), labels)
+ counter.addLabels(x, labels)
}
}
@@ -122,7 +120,7 @@ func (counter *Counter) fastAddUint64(x uint64) {
// is the first Add. Create a dummy
// counterValue for the no-labels value.
// Dummy counterValue simplifies display in WriteMetric.
- counter.Add(0, nil)
+ counter.addLabels(0, nil)
}
}
@@ -191,9 +189,9 @@ func (counter *Counter) WriteMetric(buf *strings.Builder, parentLabels string) {
buf.WriteString("} ")
value := l.counter
if len(l.labels) == 0 {
- value += float64(atomic.LoadUint64(&counter.intValue))
+ value += atomic.LoadUint64(&counter.intValue)
}
- buf.WriteString(strconv.FormatFloat(value, 'f', -1, 32))
+ buf.WriteString(strconv.FormatUint(value, 10))
buf.WriteString("\n")
}
}
@@ -210,12 +208,12 @@ func (counter *Counter) AddMetric(values map[string]float64) {
for _, l := range counter.values {
sum := l.counter
if len(l.labels) == 0 {
- sum += float64(atomic.LoadUint64(&counter.intValue))
+ sum += atomic.LoadUint64(&counter.intValue)
}
var suffix string
if len(l.formattedLabels) > 0 {
suffix = ":" + l.formattedLabels
}
- values[sanitizeTelemetryName(counter.name+suffix)] = sum
+ values[sanitizeTelemetryName(counter.name+suffix)] = float64(sum)
}
}
diff --git a/util/metrics/counterCommon.go b/util/metrics/counterCommon.go
index 26ef7f924..2a810ace6 100644
--- a/util/metrics/counterCommon.go
+++ b/util/metrics/counterCommon.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -35,7 +35,7 @@ type Counter struct {
}
type counterValues struct {
- counter float64
+ counter uint64
labels map[string]string
formattedLabels string
}
diff --git a/util/metrics/counter_test.go b/util/metrics/counter_test.go
index 72e1d3b1a..fe7d553e4 100644
--- a/util/metrics/counter_test.go
+++ b/util/metrics/counter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -144,8 +144,8 @@ func TestMetricCounterMixed(t *testing.T) {
counter := MakeCounter(MetricName{Name: "metric_test_name1", Description: "this is the metric test for counter object"})
- counter.Add(5.25, nil)
- counter.Add(8.25, map[string]string{})
+ counter.AddUint64(5, nil)
+ counter.AddUint64(8, map[string]string{})
for i := 0; i < 20; i++ {
counter.Inc(nil)
// wait half-a cycle
@@ -169,7 +169,7 @@ func TestMetricCounterMixed(t *testing.T) {
for k, v := range test.metrics {
// we have increased each one of the labels exactly 4 times. See that the counter was counting correctly.
// ( counters starts at zero )
- require.Equal(t, "35.5", v, fmt.Sprintf("The metric '%s' reached value '%s'", k, v))
+ require.Equal(t, "35", v, fmt.Sprintf("The metric '%s' reached value '%s'", k, v))
}
}
@@ -188,13 +188,13 @@ testname{host="myhost"} 0
`
require.Equal(t, expected, sbOut.String())
- c.Add(2.3, nil)
+ c.AddUint64(2, nil)
// ensure non-zero counters are logged
sbOut = strings.Builder{}
c.WriteMetric(&sbOut, `host="myhost"`)
expected = `# HELP testname testhelp
# TYPE testname counter
-testname{host="myhost"} 2.3
+testname{host="myhost"} 2
`
require.Equal(t, expected, sbOut.String())
}
diff --git a/util/metrics/gauge.go b/util/metrics/gauge.go
index f37896775..ce203d47c 100644
--- a/util/metrics/gauge.go
+++ b/util/metrics/gauge.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -19,16 +19,14 @@ package metrics
import (
"strconv"
"strings"
-
- "github.com/algorand/go-deadlock"
+ "sync/atomic"
)
// Gauge represent a single gauge variable.
type Gauge struct {
- deadlock.Mutex
+ value uint64
name string
description string
- value float64
}
// MakeGauge create a new gauge with the provided name and description.
@@ -60,24 +58,17 @@ func (gauge *Gauge) Deregister(reg *Registry) {
}
// Add increases gauge by x
-func (gauge *Gauge) Add(x float64) {
- gauge.Lock()
- defer gauge.Unlock()
- gauge.value += x
+func (gauge *Gauge) Add(x uint64) {
+ atomic.AddUint64(&gauge.value, x)
}
// Set sets gauge to x
-func (gauge *Gauge) Set(x float64) {
- gauge.Lock()
- defer gauge.Unlock()
- gauge.value = x
+func (gauge *Gauge) Set(x uint64) {
+ atomic.StoreUint64(&gauge.value, x)
}
// WriteMetric writes the metric into the output stream
func (gauge *Gauge) WriteMetric(buf *strings.Builder, parentLabels string) {
- gauge.Lock()
- defer gauge.Unlock()
-
buf.WriteString("# HELP ")
buf.WriteString(gauge.name)
buf.WriteString(" ")
@@ -91,14 +82,14 @@ func (gauge *Gauge) WriteMetric(buf *strings.Builder, parentLabels string) {
buf.WriteString(parentLabels)
}
buf.WriteString("} ")
- buf.WriteString(strconv.FormatFloat(gauge.value, 'f', -1, 32))
+ value := atomic.LoadUint64(&gauge.value)
+ buf.WriteString(strconv.FormatUint(value, 10))
buf.WriteString("\n")
}
// AddMetric adds the metric into the map
func (gauge *Gauge) AddMetric(values map[string]float64) {
- gauge.Lock()
- defer gauge.Unlock()
+ value := atomic.LoadUint64(&gauge.value)
- values[sanitizeTelemetryName(gauge.name)] = gauge.value
+ values[sanitizeTelemetryName(gauge.name)] = float64(value)
}
diff --git a/util/metrics/gauge_test.go b/util/metrics/gauge_test.go
index 41a9edb92..afa0bbd59 100644
--- a/util/metrics/gauge_test.go
+++ b/util/metrics/gauge_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -52,8 +52,8 @@ func TestMetricGauge(t *testing.T) {
gauges[i] = MakeGauge(MetricName{Name: fmt.Sprintf("gauge_%d", i), Description: "this is the metric test for gauge object"})
}
for i := 0; i < 9; i++ {
- gauges[i%3].Set(float64(i * 100))
- gauges[i%3].Add(float64(i))
+ gauges[i%3].Set(uint64(i * 100))
+ gauges[i%3].Add(uint64(i))
// wait half-a cycle
time.Sleep(test.sampleRate / 2)
}
diff --git a/util/metrics/metrics.go b/util/metrics/metrics.go
index 06a0431ec..11973e85b 100644
--- a/util/metrics/metrics.go
+++ b/util/metrics/metrics.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -95,6 +95,8 @@ var (
TransactionMessagesAlreadyCommitted = MetricName{Name: "algod_transaction_messages_err_or_committed", Description: "Number of duplicate or error transaction messages after TX handler backlog"}
// TransactionMessagesTxGroupInvalidFee "Number of transaction messages with invalid txgroup fee"
TransactionMessagesTxGroupInvalidFee = MetricName{Name: "algod_transaction_messages_txgroup_invalid_fee", Description: "Number of transaction messages with invalid txgroup fee"}
+ // TransactionMessagesTxnDroppedCongestionManagement "Number of transaction messages dropped because the tx backlog is under congestion management"
+ TransactionMessagesTxnDroppedCongestionManagement = MetricName{Name: "algod_transaction_messages_txn_dropped_congestion_ctrl", Description: "Number of transaction messages dropped because the tx backlog is under congestion management"}
// TransactionMessagesTxnNotWellFormed "Number of transaction messages not well formed"
TransactionMessagesTxnNotWellFormed = MetricName{Name: "algod_transaction_messages_txn_notwell_formed", Description: "Number of transaction messages not well formed"}
// TransactionMessagesTxnSigNotWellFormed "Number of transaction messages with bad formed signature"
diff --git a/util/metrics/metrics_test.go b/util/metrics/metrics_test.go
index 2e2828b1f..84cd4292f 100644
--- a/util/metrics/metrics_test.go
+++ b/util/metrics/metrics_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/registry.go b/util/metrics/registry.go
index 53ada420a..2f727aaab 100644
--- a/util/metrics/registry.go
+++ b/util/metrics/registry.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/registryCommon.go b/util/metrics/registryCommon.go
index 8a0f53464..848ddc836 100644
--- a/util/metrics/registryCommon.go
+++ b/util/metrics/registryCommon.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/registry_test.go b/util/metrics/registry_test.go
index 2256993f2..17328f1cb 100644
--- a/util/metrics/registry_test.go
+++ b/util/metrics/registry_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -29,17 +29,17 @@ func TestWriteAdd(t *testing.T) {
// Test AddMetrics and WriteMetrics with a counter
counter := MakeCounter(MetricName{Name: "gauge-name", Description: "gauge description"})
- counter.Add(12.34, nil)
+ counter.AddUint64(12, nil)
labelCounter := MakeCounter(MetricName{Name: "label-counter", Description: "counter with labels"})
- labelCounter.Add(5, map[string]string{"label": "a label value"})
+ labelCounter.AddUint64(5, map[string]string{"label": "a label value"})
results := make(map[string]float64)
DefaultRegistry().AddMetrics(results)
require.Equal(t, 2, len(results), "results", results)
require.Contains(t, results, "gauge-name")
- require.InDelta(t, 12.34, results["gauge-name"], 0.01)
+ require.InDelta(t, 12, results["gauge-name"], 0.01)
require.Contains(t, results, "label-counter_label__a_label_value_")
require.InDelta(t, 5, results["label-counter_label__a_label_value_"], 0.01)
@@ -50,7 +50,7 @@ func TestWriteAdd(t *testing.T) {
DefaultRegistry().AddMetrics(results)
require.Contains(t, results, "gauge-name")
- require.InDelta(t, 12.34, results["gauge-name"], 0.01)
+ require.InDelta(t, 12, results["gauge-name"], 0.01)
// not included in string builder
bufAfter := strings.Builder{}
diff --git a/util/metrics/reporter.go b/util/metrics/reporter.go
index efecf6f65..19aef0c36 100644
--- a/util/metrics/reporter.go
+++ b/util/metrics/reporter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/reporter_test.go b/util/metrics/reporter_test.go
index 7339c708d..4c522a9b2 100755
--- a/util/metrics/reporter_test.go
+++ b/util/metrics/reporter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/runtime.go b/util/metrics/runtime.go
index 3f89ea761..29d33e781 100644
--- a/util/metrics/runtime.go
+++ b/util/metrics/runtime.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/runtime_test.go b/util/metrics/runtime_test.go
index 103248446..32908670d 100644
--- a/util/metrics/runtime_test.go
+++ b/util/metrics/runtime_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/service.go b/util/metrics/service.go
index e37f5c734..5b1e39527 100644
--- a/util/metrics/service.go
+++ b/util/metrics/service.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/serviceCommon.go b/util/metrics/serviceCommon.go
index e0b26c820..947e9c440 100644
--- a/util/metrics/serviceCommon.go
+++ b/util/metrics/serviceCommon.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/tagcounter.go b/util/metrics/tagcounter.go
index 80689e7a8..1daf30ba1 100644
--- a/util/metrics/tagcounter.go
+++ b/util/metrics/tagcounter.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/metrics/tagcounter_test.go b/util/metrics/tagcounter_test.go
index 9e8a50717..8c5991e19 100644
--- a/util/metrics/tagcounter_test.go
+++ b/util/metrics/tagcounter_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/process.go b/util/process.go
index 4ea34796a..6a6d17d4a 100644
--- a/util/process.go
+++ b/util/process.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/process_common.go b/util/process_common.go
index b2c1bc708..03af826b0 100644
--- a/util/process_common.go
+++ b/util/process_common.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/process_windows.go b/util/process_windows.go
index 4cad60df7..7fe14d974 100644
--- a/util/process_windows.go
+++ b/util/process_windows.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/rateLimit.go b/util/rateLimit.go
new file mode 100644
index 000000000..c4e85c71e
--- /dev/null
+++ b/util/rateLimit.go
@@ -0,0 +1,556 @@
+// 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 util
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math"
+ "math/rand"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/algorand/go-algorand/util/metrics"
+ "github.com/algorand/go-deadlock"
+)
+
+var errConManDropped = errors.New("congestionManager prevented client from consuming capacity")
+var errFailedConsume = errors.New("could not consume capacity from capacityQueue")
+var errERLReservationExists = errors.New("client already has a reservation")
+var errCapacityReturn = errors.New("could not replace capacity to channel")
+
+// ElasticRateLimiter holds and distributes capacity through capacityQueues
+// Capacity consumers are given an error if there is no capacity available for them,
+// and a "capacityGuard" structure they can use to return the capacity when finished
+type ElasticRateLimiter struct {
+ MaxCapacity int
+ CapacityPerReservation int
+ sharedCapacity capacityQueue
+ capacityByClient map[ErlClient]capacityQueue
+ clientLock deadlock.RWMutex
+ // CongestionManager and enable flag
+ cm CongestionManager
+ enableCM bool
+ congestionControlCounter *metrics.Counter
+}
+
+// ErlClient clients must support OnClose for reservation closing
+type ErlClient interface {
+ OnClose(func())
+}
+
+// capacity is an empty structure used for loading and draining queues
+type capacity struct {
+}
+
+// Capacity Queue wraps and maintains a channel of opaque capacity structs
+type capacityQueue chan capacity
+
+// ErlCapacityGuard is the structure returned to clients so they can release the capacity when needed
+// they also inform the congestion manager of events
+type ErlCapacityGuard struct {
+ cq capacityQueue
+ cm CongestionManager
+}
+
+// Release will put capacity back into the queue attached to this capacity guard
+func (cg *ErlCapacityGuard) Release() error {
+ if cg.cq == nil {
+ return nil
+ }
+ select {
+ case cg.cq <- capacity{}:
+ return nil
+ default:
+ return errCapacityReturn
+ }
+}
+
+// Served will notify the CongestionManager that this resource has been served, informing the Service Rate
+func (cg *ErlCapacityGuard) Served() {
+ if cg.cm != nil {
+ cg.cm.Served(time.Now())
+ }
+}
+
+func (q capacityQueue) blockingRelease() {
+ q <- capacity{}
+}
+
+func (q capacityQueue) blockingConsume() {
+ <-q
+}
+
+func (q capacityQueue) consume(cm CongestionManager) (ErlCapacityGuard, error) {
+ select {
+ case <-q:
+ return ErlCapacityGuard{
+ cq: q,
+ cm: cm,
+ }, nil
+ default:
+ return ErlCapacityGuard{}, errFailedConsume
+ }
+}
+
+// NewElasticRateLimiter creates an ElasticRateLimiter and initializes maps
+// maxCapacity: the total (absolute maximum) number of capacity units vended by this ERL at a given time
+// reservedCapacity: the number of capacity units to be reserved per client
+// cmWindow: the window duration of data collection for congestion management, passed to the congestion manager
+// conmanCount: the metric to increment when the congestion manager proposes dropping a request
+func NewElasticRateLimiter(
+ maxCapacity int,
+ reservedCapacity int,
+ cmWindow time.Duration,
+ conmanCount *metrics.Counter) *ElasticRateLimiter {
+ ret := ElasticRateLimiter{
+ MaxCapacity: maxCapacity,
+ CapacityPerReservation: reservedCapacity,
+ capacityByClient: map[ErlClient]capacityQueue{},
+ sharedCapacity: capacityQueue(make(chan capacity, maxCapacity)),
+ congestionControlCounter: conmanCount,
+ }
+ congestionManager := NewREDCongestionManager(
+ cmWindow,
+ maxCapacity)
+ ret.cm = congestionManager
+ // fill the sharedCapacity
+ for i := 0; i < maxCapacity; i++ {
+ ret.sharedCapacity.blockingRelease()
+ }
+ return &ret
+}
+
+// Start will start any underlying component of the ElasticRateLimiter
+func (erl *ElasticRateLimiter) Start() {
+ if erl.cm != nil {
+ erl.cm.Start()
+ }
+}
+
+// Stop will stop any underlying component of the ElasticRateLimiter
+func (erl *ElasticRateLimiter) Stop() {
+ if erl.cm != nil {
+ erl.cm.Stop()
+ }
+}
+
+// EnableCongestionControl turns on the flag that the ERL uses to check with its CongestionManager
+func (erl *ElasticRateLimiter) EnableCongestionControl() {
+ erl.clientLock.Lock()
+ defer erl.clientLock.Unlock()
+ erl.enableCM = true
+}
+
+// DisableCongestionControl turns off the flag that the ERL uses to check with its CongestionManager
+func (erl *ElasticRateLimiter) DisableCongestionControl() {
+ erl.clientLock.Lock()
+ defer erl.clientLock.Unlock()
+ erl.enableCM = false
+}
+
+// ConsumeCapacity will dispense one capacity from either the resource's reservedCapacity,
+// and will return a guard who can return capacity when the client is ready
+// Returns an error if the capacity could not be vended, which could be:
+// - there is not sufficient free capacity to assign a reserved capacity block
+// - there is no reserved or shared capacity available for the client
+func (erl *ElasticRateLimiter) ConsumeCapacity(c ErlClient) (*ErlCapacityGuard, error) {
+ var q capacityQueue
+ var err error
+ var exists bool
+ var enableCM bool
+ // get the client's queue
+ erl.clientLock.RLock()
+ q, exists = erl.capacityByClient[c]
+ enableCM = erl.enableCM
+ erl.clientLock.RUnlock()
+
+ // Step 0: Check for, and create a capacity reservation if needed
+ if !exists {
+ q, err = erl.openReservation(c)
+ if err != nil {
+ return nil, err
+ }
+ // if the client has been given a new reservation, make sure it cleans up OnClose
+ c.OnClose(func() { erl.closeReservation(c) })
+
+ // if this reservation is newly created, directly (blocking) take a capacity
+ q.blockingConsume()
+ return &ErlCapacityGuard{cq: q, cm: erl.cm}, nil
+ }
+
+ // Step 1: Attempt consumption from the reserved queue
+ cg, err := q.consume(erl.cm)
+ if err == nil {
+ if erl.cm != nil {
+ erl.cm.Consumed(c, time.Now()) // notify the congestion manager that this client consumed from this queue
+ }
+ return &cg, nil
+ }
+ // Step 2: Potentially gate shared queue access if the congestion manager disallows it
+ if erl.cm != nil &&
+ enableCM &&
+ erl.cm.ShouldDrop(c) {
+ if erl.congestionControlCounter != nil {
+ erl.congestionControlCounter.Inc(nil)
+ }
+ return nil, errConManDropped
+ }
+ // Step 3: Attempt consumption from the shared queue
+ cg, err = erl.sharedCapacity.consume(erl.cm)
+ if err != nil {
+ return nil, err
+ }
+ if erl.cm != nil {
+ erl.cm.Consumed(c, time.Now()) // notify the congestion manager that this client consumed from this queue
+ }
+ return &cg, nil
+}
+
+// openReservation creates an entry in the ElasticRateLimiter's reservedCapacity map,
+// and optimistically transfers capacity from the sharedCapacity to the reservedCapacity
+func (erl *ElasticRateLimiter) openReservation(c ErlClient) (capacityQueue, error) {
+ erl.clientLock.Lock()
+ defer erl.clientLock.Unlock()
+ if _, exists := erl.capacityByClient[c]; exists {
+ return capacityQueue(nil), errERLReservationExists
+ }
+ // guard against overprovisioning, if there is less than a reservedCapacity amount left
+ remaining := erl.MaxCapacity - (erl.CapacityPerReservation * len(erl.capacityByClient))
+ if erl.CapacityPerReservation > remaining {
+ return capacityQueue(nil), fmt.Errorf("not enough capacity to reserve for client: %d remaining, %d requested", remaining, erl.CapacityPerReservation)
+ }
+ // make capacity for the provided client
+ q := capacityQueue(make(chan capacity, erl.CapacityPerReservation))
+ erl.capacityByClient[c] = q
+ // create a thread to drain the capacity from sharedCapacity in a blocking way
+ // and move it to the reservation, also in a blocking way
+ go func() {
+ for i := 0; i < erl.CapacityPerReservation; i++ {
+ erl.sharedCapacity.blockingConsume()
+ q.blockingRelease()
+ }
+ }()
+ return q, nil
+}
+
+// closeReservation will remove the client mapping to capacity channel,
+// and will kick off a routine to drain the capacity and replace it to the shared capacity
+func (erl *ElasticRateLimiter) closeReservation(c ErlClient) {
+ erl.clientLock.Lock()
+ defer erl.clientLock.Unlock()
+ q, exists := erl.capacityByClient[c]
+ // guard clauses, and preventing the ElasticRateLimiter from draining its own sharedCapacity
+ if !exists || q == erl.sharedCapacity {
+ return
+ }
+ delete(erl.capacityByClient, c)
+ // start a routine to consume capacity from the closed reservation, and return it to the sharedCapacity
+ go func() {
+ for i := 0; i < erl.CapacityPerReservation; i++ {
+ q.blockingConsume()
+ erl.sharedCapacity.blockingRelease()
+ }
+ }()
+}
+
+// CongestionManager is an interface for tracking events which happen to capacityQueues
+type CongestionManager interface {
+ Start()
+ Stop()
+ Consumed(c ErlClient, t time.Time)
+ Served(t time.Time)
+ ShouldDrop(c ErlClient) bool
+}
+
+type event struct {
+ c ErlClient
+ t time.Time
+}
+
+type shouldDropQuery struct {
+ c ErlClient
+ ret chan bool
+}
+
+// "Random Early Detection" congestion manager,
+// will propose to drop messages proportional to the caller's request rate vs Average Service Rate
+type redCongestionManager struct {
+ runLock *deadlock.Mutex
+ running bool
+ window time.Duration
+ consumed chan event
+ served chan event
+ shouldDropQueries chan shouldDropQuery
+ targetRate float64
+ targetRateRefreshTicks int
+ // exp is applied as an exponential factor in shouldDrop. 1 would be linearly proportional, higher values punish noisy neighbors more
+ exp float64
+ // consumed is the only value tracked by-queue. The others are calculated in-total
+ // TODO: If we desire later, we can add mappings onto release/done for more insight
+ consumedByClient map[ErlClient]*[]time.Time
+ serves []time.Time
+ // synchronization for unit tests
+ ctx context.Context
+ ctxCancel context.CancelFunc
+ wg sync.WaitGroup
+}
+
+// NewREDCongestionManager creates a Congestion Manager which will watches capacityGuard activity,
+// and regularly calculates a Target Service Rate, and can give "Should Drop" suggestions
+func NewREDCongestionManager(d time.Duration, bsize int) *redCongestionManager {
+ ret := redCongestionManager{
+ runLock: &deadlock.Mutex{},
+ window: d,
+ consumed: make(chan event, bsize),
+ served: make(chan event, bsize),
+ shouldDropQueries: make(chan shouldDropQuery, bsize),
+ targetRateRefreshTicks: bsize / 10, // have the Congestion Manager refresh its target rates every 10% through the queue
+ consumedByClient: map[ErlClient]*[]time.Time{},
+ exp: 4,
+ wg: sync.WaitGroup{},
+ }
+ return &ret
+}
+
+// Consumed implements CongestionManager by putting an event on the consumed channel,
+// to be processed by the Start() loop
+func (cm *redCongestionManager) Consumed(c ErlClient, t time.Time) {
+ select {
+ case cm.consumed <- event{
+ c: c,
+ t: t,
+ }:
+ default:
+ }
+}
+
+// Served implements CongestionManager by putting an event on the done channel,
+// to be processed by the Start() loop
+func (cm *redCongestionManager) Served(t time.Time) {
+ select {
+ case cm.served <- event{
+ t: t,
+ }:
+ default:
+ }
+}
+
+// ShouldDrop implements CongestionManager by putting a query shouldDropQueries channel,
+// and blocks on the response to return synchronously to the caller
+// if an error should prevent the query from running, the result is defaulted to false
+func (cm *redCongestionManager) ShouldDrop(c ErlClient) bool {
+ ret := make(chan bool)
+ select {
+ case cm.shouldDropQueries <- shouldDropQuery{
+ c: c,
+ ret: ret,
+ }:
+ return <-ret
+ default:
+ return false
+ }
+}
+
+// Start will kick off a goroutine to consume activity from the different activity channels,
+// as well as service queries about if a given capacityQueue should drop
+func (cm *redCongestionManager) Start() {
+ // check if the maintainer is already running to ensure there is only one routine
+ cm.runLock.Lock()
+ defer cm.runLock.Unlock()
+ if cm.running {
+ return
+ }
+ cm.ctx, cm.ctxCancel = context.WithCancel(context.Background())
+ cm.running = true
+ cm.wg.Add(1)
+ go cm.run()
+}
+
+func (cm *redCongestionManager) run() {
+ tick := 0
+ targetRate := float64(0)
+ consumedByClient := map[ErlClient]*[]time.Time{}
+ serves := []time.Time{}
+ lastServiceRateUpdate := time.Now()
+ exit := false
+ for {
+ select {
+ // prioritize shouldDropQueries
+ case query := <-cm.shouldDropQueries:
+ cutoff := time.Now().Add(-1 * cm.window)
+ prune(consumedByClient[query.c], cutoff)
+ query.ret <- cm.shouldDrop(targetRate, query.c, consumedByClient[query.c])
+ default:
+ select {
+ // "should drop" queries
+ case query := <-cm.shouldDropQueries:
+ cutoff := time.Now().Add(-1 * cm.window)
+ prune(consumedByClient[query.c], cutoff)
+ query.ret <- cm.shouldDrop(targetRate, query.c, consumedByClient[query.c])
+ // consumed events -- a client has consumed capacity from a queue
+ case e := <-cm.consumed:
+ if consumedByClient[e.c] == nil {
+ ts := []time.Time{}
+ consumedByClient[e.c] = &ts
+ }
+ *(consumedByClient[e.c]) = append(*(consumedByClient[e.c]), e.t)
+ // served events -- the capacity has been totally served
+ case e := <-cm.served:
+ serves = append(serves, e.t)
+ // check for context Done, and flag the thread for shutdown
+ case <-cm.ctx.Done():
+ exit = true
+ }
+
+ }
+ // recalculate the service rate every N ticks, or every 100ms
+ // also calculate if the routine is going to exit
+ tick = (tick + 1) % cm.targetRateRefreshTicks
+ if tick == 0 || time.Now().After(lastServiceRateUpdate.Add(100*time.Millisecond)) || exit {
+ lastServiceRateUpdate = time.Now()
+ cutoff := time.Now().Add(-1 * cm.window)
+ prune(&serves, cutoff)
+ for c := range consumedByClient {
+ if prune(consumedByClient[c], cutoff) == 0 {
+ delete(consumedByClient, c)
+ }
+ }
+ targetRate = 0
+ // targetRate is the average service rate per client per second
+ if len(consumedByClient) > 0 {
+ serviceRate := float64(len(serves)) / float64(cm.window/time.Second)
+ targetRate = serviceRate / float64(len(consumedByClient))
+ }
+ }
+ if exit {
+ cm.setTargetRate(targetRate)
+ cm.setConsumedByClient(consumedByClient)
+ cm.setServes(serves)
+ cm.runLock.Lock()
+ defer cm.runLock.Unlock()
+ cm.running = false
+ cm.wg.Done()
+ return
+ }
+ }
+}
+
+func (cm *redCongestionManager) Stop() {
+ cm.ctxCancel()
+ cm.wg.Wait()
+}
+
+func (cm *redCongestionManager) setTargetRate(tr float64) {
+ cm.targetRate = tr
+}
+
+func (cm *redCongestionManager) setConsumedByClient(cbc map[ErlClient]*[]time.Time) {
+ cm.consumedByClient = cbc
+}
+
+func (cm *redCongestionManager) setServes(ts []time.Time) {
+ cm.serves = ts
+}
+
+func (cm *redCongestionManager) arrivalRateFor(arrivals *[]time.Time) float64 {
+ clientArrivalRate := float64(0)
+ if arrivals != nil {
+ clientArrivalRate = float64(len(*arrivals)) / float64(cm.window/time.Second)
+ }
+ return clientArrivalRate
+}
+
+// shouldDrop ultimately makes the recommendation to drop a given request through some fairness probability.
+// Comparing this behavior with the behavior of a basic Random Early Detection system:
+// A standard RED model will drop any message with chance proportional to its queue's fullness. The more full, the more random dropping is applied to all clients.
+// In this RED model, there is an application of fairness, in which the chance a client's request is dropped is proportional to their individual arrival rate, vs a per-client service rate.
+// A behavior example is as follows:
+// client1 makes 100 requests over a given sliding window (10s for this example)
+// client2 makes 200 requests over the window
+// all 300 requests were served over the window
+//
+// This means:
+// - client1's arrival rate is 100/10 = 10/s
+// - client2's arrival rate is 200/10 = 20/s
+// - the service rate is 300/10 = 30/s
+// - the *target rate* is the service rate per client: 30/2 = 15/s
+//
+// When a shouldDrop request is made:
+// - client1 shouldDrop: 10 / 15 > random float ?
+// - client2 shouldDrop: 20 / 15 > random float ?
+// - Additionally, the arrival and service rates are raised to an exponential power, to increase contrast.
+//
+// client2 will be throttled because it is making requests in excess of its target rate.
+// client1 will be throttled proportional to its usage of the service rate.
+// over time, client2 will fall in line with the appropriate service rate, while other clients will be able to use the newly freed capacity
+// The net effect is that clients who are disproportionately noisy are dropped more often,
+// while quieter ones are are dropped less often.
+// The reason this works is that the serviceRate represents the ability for the given resource to be serviced (ie, the rate at which work is dequeued).
+// When congestion management is required, the service should attempt a fair distribution of servicing to all clients.
+// clients who are making requests in excess of our known ability to fairly service requests should be reduced.
+func (cm *redCongestionManager) shouldDrop(targetRate float64, c ErlClient, arrivals *[]time.Time) bool {
+ // clients who have "never" been seen do not get dropped
+ clientArrivalRate := cm.arrivalRateFor(arrivals)
+ if clientArrivalRate == 0 {
+ return false
+ }
+ // if targetRate is 0, it means we haven't had any activity to calculate (or there is not enough data)
+ // it should not drop in this case
+ if targetRate == 0 {
+ return false
+ }
+ // A random float is selected, and the arrival rate of the given client is
+ // turned to a ratio against targetRate. the congestion manager recommends to drop activity
+ // proportional to its overuse above the targetRate
+ r := rand.Float64()
+ return (math.Pow(clientArrivalRate, cm.exp) / math.Pow(targetRate, cm.exp)) > r
+}
+
+func prune(ts *[]time.Time, cutoff time.Time) int {
+ // guard against nil lists
+ if ts == nil {
+ return 0
+ }
+ // guard against empty lists
+ if len(*ts) == 0 {
+ return 0
+ }
+ // optimization: if the last element falls before the cutoff, prune the whole list without iteration
+ if (*ts)[len(*ts)-1].Before(cutoff) {
+ *ts = (*ts)[:0]
+ return 0
+ }
+ // optimization: if the list is longer than 50 elements, use a binary search to find the cutoff line
+ if len(*ts) > 50 {
+ i := sort.Search(len(*ts), func(i int) bool { return (*ts)[i].After(cutoff) })
+ *ts = (*ts)[i:]
+ return len(*ts)
+ }
+ // find the first inserted timestamp *after* the cutoff, and cut everything behind it off
+ for i, t := range *ts {
+ if t.After(cutoff) {
+ *ts = (*ts)[i:]
+ return len(*ts)
+ }
+ }
+ // if no values are after the cutoff, clear the array and give back a 0
+ *ts = (*ts)[:0]
+ return 0
+}
diff --git a/util/rateLimit_test.go b/util/rateLimit_test.go
new file mode 100644
index 000000000..669960ecf
--- /dev/null
+++ b/util/rateLimit_test.go
@@ -0,0 +1,291 @@
+// 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 util
+
+import (
+ "testing"
+ "time"
+
+ "github.com/algorand/go-algorand/test/partitiontest"
+ "github.com/stretchr/testify/assert"
+)
+
+type mockClient string
+
+type mockCongestionControl struct{}
+
+func (cg mockCongestionControl) Start() {}
+func (cg mockCongestionControl) Stop() {}
+func (cg mockCongestionControl) Consumed(c ErlClient, t time.Time) {}
+func (cg mockCongestionControl) Served(t time.Time) {}
+func (cg mockCongestionControl) ShouldDrop(c ErlClient) bool { return true }
+
+func (c mockClient) OnClose(func()) {
+ return
+}
+
+func TestNewElasticRateLimiter(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ erl := NewElasticRateLimiter(100, 10, time.Second, nil)
+
+ assert.Equal(t, len(erl.sharedCapacity), 100)
+ assert.Equal(t, len(erl.capacityByClient), 0)
+}
+
+func TestElasticRateLimiterCongestionControlled(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ client := mockClient("client")
+ erl := NewElasticRateLimiter(3, 2, time.Second, nil)
+ // give the ERL a congestion controler with well defined behavior for testing
+ erl.cm = mockCongestionControl{}
+
+ _, err := erl.ConsumeCapacity(client)
+ // because the ERL gives capacity to a reservation, and then asynchronously drains capacity from the share,
+ // wait a moment before testing the size of the sharedCapacity
+ time.Sleep(100 * time.Millisecond)
+ assert.Equal(t, 1, len(erl.capacityByClient[client]))
+ assert.Equal(t, 1, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+
+ erl.EnableCongestionControl()
+ _, err = erl.ConsumeCapacity(client)
+ assert.Equal(t, 0, len(erl.capacityByClient[client]))
+ assert.Equal(t, 1, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+
+ _, err = erl.ConsumeCapacity(client)
+ assert.Equal(t, 0, len(erl.capacityByClient[client]))
+ assert.Equal(t, 1, len(erl.sharedCapacity))
+ assert.Error(t, err)
+
+ erl.DisableCongestionControl()
+ _, err = erl.ConsumeCapacity(client)
+ assert.Equal(t, 0, len(erl.capacityByClient[client]))
+ assert.Equal(t, 0, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+}
+
+func TestReservations(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ client1 := mockClient("client1")
+ client2 := mockClient("client2")
+ erl := NewElasticRateLimiter(4, 1, time.Second, nil)
+
+ _, err := erl.ConsumeCapacity(client1)
+ // because the ERL gives capacity to a reservation, and then asynchronously drains capacity from the share,
+ // wait a moment before testing the size of the sharedCapacity
+ time.Sleep(100 * time.Millisecond)
+ assert.Equal(t, 1, len(erl.capacityByClient))
+ assert.NoError(t, err)
+
+ _, err = erl.ConsumeCapacity(client2)
+ // because the ERL gives capacity to a reservation, and then asynchronously drains capacity from the share,
+ // wait a moment before testing the size of the sharedCapacity
+ time.Sleep(100 * time.Millisecond)
+ assert.Equal(t, 2, len(erl.capacityByClient))
+ assert.NoError(t, err)
+
+ erl.closeReservation(client1)
+ assert.Equal(t, 1, len(erl.capacityByClient))
+ erl.closeReservation(client2)
+ assert.Equal(t, 0, len(erl.capacityByClient))
+}
+
+func TestConsumeReleaseCapacity(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ client := mockClient("client")
+ erl := NewElasticRateLimiter(4, 3, time.Second, nil)
+
+ c1, err := erl.ConsumeCapacity(client)
+ // because the ERL gives capacity to a reservation, and then asynchronously drains capacity from the share,
+ // wait a moment before testing the size of the sharedCapacity
+ time.Sleep(100 * time.Millisecond)
+ assert.Equal(t, 2, len(erl.capacityByClient[client]))
+ assert.Equal(t, 1, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+
+ _, err = erl.ConsumeCapacity(client)
+ assert.Equal(t, 1, len(erl.capacityByClient[client]))
+ assert.Equal(t, 1, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+
+ _, err = erl.ConsumeCapacity(client)
+ assert.Equal(t, 0, len(erl.capacityByClient[client]))
+ assert.Equal(t, 1, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+
+ // remember this capacity, as it is a shared capacity
+ c4, err := erl.ConsumeCapacity(client)
+ assert.Equal(t, 0, len(erl.capacityByClient[client]))
+ assert.Equal(t, 0, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+
+ _, err = erl.ConsumeCapacity(client)
+ assert.Equal(t, 0, len(erl.capacityByClient[client]))
+ assert.Equal(t, 0, len(erl.sharedCapacity))
+ assert.Error(t, err)
+
+ // now release the capacity and observe the items return to the correct places
+ err = c1.Release()
+ assert.Equal(t, 1, len(erl.capacityByClient[client]))
+ assert.Equal(t, 0, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+
+ // now release the capacity and observe the items return to the correct places
+ err = c4.Release()
+ assert.Equal(t, 1, len(erl.capacityByClient[client]))
+ assert.Equal(t, 1, len(erl.sharedCapacity))
+ assert.NoError(t, err)
+
+}
+
+func TestREDCongestionManagerShouldDrop(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ client := mockClient("client")
+ other := mockClient("other")
+ red := NewREDCongestionManager(time.Second*10, 10000)
+ // calculate the target rate every request for most accurate results
+ red.targetRateRefreshTicks = 1
+ red.Start()
+ // indicate that the arrival rate is essentially 1/s
+ for i := 0; i < 10; i++ {
+ red.Consumed(client, time.Now())
+ }
+ // indicate that the service rate is essentially 0.9/s
+ for i := 0; i < 9; i++ {
+ red.Served(time.Now())
+ }
+ // allow the statistics to catch up before asserting
+ time.Sleep(100 * time.Millisecond)
+ // the service rate should be 0.9/s, and the arrival rate for this client should be 1/s
+ // for this reason, it should always drop the message
+ for i := 0; i < 100; i++ {
+ assert.True(t, red.ShouldDrop(client))
+ }
+ // this caller hasn't consumed any capacity before, so it won't need to drop
+ for i := 0; i < 10; i++ {
+ assert.False(t, red.ShouldDrop(other))
+ }
+ // allow the congestion manager to consume and process the given messages
+ time.Sleep(100 * time.Millisecond)
+ red.Stop()
+ assert.Equal(t, 10, len(*red.consumedByClient[client]))
+ assert.Equal(t, float64(1), red.arrivalRateFor(red.consumedByClient[client]))
+ assert.Equal(t, 0.0, red.arrivalRateFor(red.consumedByClient[other]))
+ assert.Equal(t, 0.9, red.targetRate)
+}
+
+func TestREDCongestionManagerShouldntDrop(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ client := mockClient("client")
+ red := NewREDCongestionManager(time.Second*10, 10000)
+ // calculate the target rate every request for most accurate results
+ red.targetRateRefreshTicks = 1
+ red.Start()
+
+ // indicate that the arrival rate is essentially 0.1/s!
+ red.Consumed(client, time.Now())
+
+ // drive 10k messages, in batches of 500, with 100ms sleeps
+ for i := 0; i < 20; i++ {
+ for j := 0; j < 500; j++ {
+ red.Served(time.Now())
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+ // the service rate should be 1000/s, and the arrival rate for this client should be 0.1/s
+ // for this reason, shouldDrop should almost certainly return false (true only 1/100k times)
+ for i := 0; i < 10; i++ {
+ assert.False(t, red.ShouldDrop(client))
+ }
+ // allow the congestion manager to consume and process the given messages
+ time.Sleep(1000 * time.Millisecond)
+ red.Stop()
+ assert.Equal(t, 1, len(*red.consumedByClient[client]))
+ assert.Equal(t, 10000, len(red.serves))
+ assert.Equal(t, 0.1, red.arrivalRateFor(red.consumedByClient[client]))
+ assert.Equal(t, float64(1000), red.targetRate)
+}
+
+func TestREDCongestionManagerTargetRate(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ client := mockClient("client")
+ red := NewREDCongestionManager(time.Second*10, 10000)
+ red.Start()
+ red.Consumed(client, time.Now())
+ red.Consumed(client, time.Now())
+ red.Consumed(client, time.Now())
+ red.Served(time.Now())
+ red.Served(time.Now())
+ red.Served(time.Now())
+ // allow the congestion manager to consume and process the given messages
+ time.Sleep(100 * time.Millisecond)
+ red.Stop()
+ assert.Equal(t, 0.3, red.arrivalRateFor(red.consumedByClient[client]))
+ assert.Equal(t, 0.3, red.targetRate)
+}
+
+func TestREDCongestionManagerPrune(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ client := mockClient("client")
+ red := NewREDCongestionManager(time.Second*10, 10000)
+ red.Start()
+ red.Consumed(client, time.Now().Add(-11*time.Second))
+ red.Consumed(client, time.Now().Add(-11*time.Second))
+ red.Consumed(client, time.Now().Add(-11*time.Second))
+ red.Consumed(client, time.Now())
+ red.Served(time.Now().Add(-11 * time.Second))
+ red.Served(time.Now().Add(-11 * time.Second))
+ red.Served(time.Now().Add(-11 * time.Second))
+ red.Served(time.Now())
+ // allow the congestion manager to consume and process the given messages
+ time.Sleep(100 * time.Millisecond)
+ red.Stop()
+ assert.Equal(t, 0.1, red.arrivalRateFor(red.consumedByClient[client]))
+ assert.Equal(t, 0.1, red.targetRate)
+}
+
+func TestREDCongestionManagerStopStart(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ client := mockClient("client")
+ red := NewREDCongestionManager(time.Second*10, 10000)
+ red.Start()
+ red.Consumed(client, time.Now())
+ red.Consumed(client, time.Now())
+ red.Consumed(client, time.Now())
+ red.Served(time.Now())
+ red.Served(time.Now())
+ red.Served(time.Now())
+ // allow the congestion manager to consume and process the given messages
+ time.Sleep(100 * time.Millisecond)
+ red.Stop()
+ assert.Equal(t, 0.3, red.arrivalRateFor(red.consumedByClient[client]))
+ assert.Equal(t, 0.3, red.targetRate)
+ // Do it all again, but with 2 calls instead of 3 and 4 serves instead of 3
+ red.Start()
+ red.Consumed(client, time.Now())
+ red.Consumed(client, time.Now())
+ red.Served(time.Now())
+ red.Served(time.Now())
+ red.Served(time.Now())
+ red.Served(time.Now())
+ // allow the congestion manager to consume and process the given messages
+ time.Sleep(100 * time.Millisecond)
+ red.Stop()
+ assert.Equal(t, 0.2, red.arrivalRateFor(red.consumedByClient[client]))
+ assert.Equal(t, 0.4, red.targetRate)
+}
diff --git a/util/s3/fileIterator.go b/util/s3/fileIterator.go
index d5adbd096..b4f4ea82d 100644
--- a/util/s3/fileIterator.go
+++ b/util/s3/fileIterator.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/s3/s3Helper.go b/util/s3/s3Helper.go
index 43f017443..8c70f14ed 100644
--- a/util/s3/s3Helper.go
+++ b/util/s3/s3Helper.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -22,6 +22,7 @@ import (
"io"
"os"
"path/filepath"
+ "reflect"
"regexp"
"runtime"
"strconv"
@@ -32,9 +33,6 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
-
- "github.com/algorand/go-algorand/util"
- "github.com/algorand/go-algorand/util/codecs"
)
const (
@@ -45,9 +43,6 @@ const (
s3DefaultReleaseBucket = "algorand-releases"
s3DefaultUploadBucket = "algorand-uploads"
s3DefaultRegion = "us-east-1"
-
- downloadAction = "download"
- uploadAction = "upload"
)
// Helper encapsulates the s3 session state for interactive with our default S3 bucket with appropriate credentials
@@ -84,20 +79,12 @@ func getS3Region() (region string) {
// MakeS3SessionForUploadWithBucket upload to bucket
func MakeS3SessionForUploadWithBucket(awsBucket string) (helper Helper, err error) {
- creds, err := getCredentials(uploadAction, awsBucket)
- if err != nil {
- return
- }
- return makeS3Session(creds, awsBucket)
+ return makeS3Session(awsBucket)
}
// MakeS3SessionForDownloadWithBucket download from bucket
func MakeS3SessionForDownloadWithBucket(awsBucket string) (helper Helper, err error) {
- creds, err := getCredentials(downloadAction, awsBucket)
- if err != nil {
- return
- }
- return makeS3Session(creds, awsBucket)
+ return makeS3Session(awsBucket)
}
// UploadFileStream sends file as stream to s3
@@ -114,84 +101,43 @@ func (helper *Helper) UploadFileStream(targetFile string, reader io.Reader) erro
return nil
}
-type s3Keys struct {
- ID string
- Secret string
-}
-
-func getCredentials(action string, awsBucket string) (creds *credentials.Credentials, err error) {
- awsID, awsKey := getAWSCredentials()
- credentailsRequired := checkCredentialsRequired(action, awsBucket)
- if !credentailsRequired && (awsID == "" || awsKey == "") {
- return credentials.AnonymousCredentials, nil
- }
- err = validateS3Credentials(awsID, awsKey)
- if err != nil {
+func validateS3Bucket(awsBucket string) (err error) {
+ if awsBucket == "" {
+ err = fmt.Errorf("bucket name is empty")
return
}
- creds = credentials.NewStaticCredentials(awsID, awsKey, "")
return
-
}
-func loadS3KeysFromFile(keyFile string) (keys s3Keys, err error) {
- err = codecs.LoadObjectFromFile(keyFile, &keys)
- return
-}
-
-func getAWSCredentials() (awsID string, awsKey string) {
- awsID, _ = os.LookupEnv("AWS_ACCESS_KEY_ID")
- awsKey, _ = os.LookupEnv("AWS_SECRET_ACCESS_KEY")
-
- // If not in environment, try to load from s3.json file in bin dir
- if awsID == "" || awsKey == "" {
- baseDir, err := util.ExeDir()
- if err == nil {
- keyFile := filepath.Join(baseDir, "s3.json")
- keys, err := loadS3KeysFromFile(keyFile)
- if err == nil {
- awsID = keys.ID
- awsKey = keys.Secret
- }
- }
- }
- return
-}
-
-func validateS3Credentials(awsID string, awsKey string) (err error) {
- if awsID == "" || awsKey == "" {
- err = fmt.Errorf("AWS credentials must be specified in environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY")
+func makeS3Session(bucket string) (helper Helper, err error) {
+ err = validateS3Bucket(bucket)
+ if err != nil {
return
}
- return
-}
-func validateS3Bucket(awsBucket string) (err error) {
- if awsBucket == "" {
- err = fmt.Errorf("bucket name is empty")
- return
+ awsConfig := &aws.Config{
+ CredentialsChainVerboseErrors: aws.Bool(true),
+ Region: aws.String(getS3Region()),
}
- return
-}
-func checkCredentialsRequired(action string, bucketName string) (required bool) {
- required = true
- if action == downloadAction && bucketName == s3DefaultReleaseBucket {
- required = false
+ // s3DefaultReleaseBucket should be public, use AnonymousCredentials
+ if bucket == s3DefaultReleaseBucket {
+ awsConfig.Credentials = credentials.AnonymousCredentials
}
- return
-}
-func makeS3Session(credentials *credentials.Credentials, bucket string) (helper Helper, err error) {
- err = validateS3Bucket(bucket)
+ sess, err := session.NewSessionWithOptions(session.Options{
+ SharedConfigState: session.SharedConfigEnable,
+ Config: *awsConfig,
+ })
if err != nil {
return
}
- sess, err := session.NewSession(&aws.Config{Region: aws.String(getS3Region()),
- Credentials: credentials})
- if err != nil {
- return
+
+ // use AnonymousCredentials if none are found
+ if creds, err := sess.Config.Credentials.Get(); err != nil && !reflect.DeepEqual(creds, credentials.AnonymousCredentials) {
+ sess.Config.Credentials = credentials.AnonymousCredentials
}
+
helper = Helper{
session: sess,
bucket: bucket,
@@ -294,7 +240,7 @@ func (helper *Helper) GetPackageFilesVersion(channel string, pkgFiles string, sp
func GetVersionFromName(name string) (version uint64, err error) {
re := regexp.MustCompile(`_(\d*)\.(\d*)\.(\d*)`)
submatchAll := re.FindAllStringSubmatch(name, -1)
- if submatchAll == nil || len(submatchAll) == 0 || len(submatchAll[0]) != 4 {
+ if len(submatchAll) == 0 || len(submatchAll[0]) != 4 {
err = errors.New("unable to parse version from filename " + name)
return
}
diff --git a/util/s3/s3Helper_test.go b/util/s3/s3Helper_test.go
index 6c4609501..c0502a3b0 100644
--- a/util/s3/s3Helper_test.go
+++ b/util/s3/s3Helper_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -114,8 +114,6 @@ func TestMakeS3SessionForUploadWithBucket(t *testing.T) {
const emptyBucket = ""
type args struct {
awsBucket string
- awsID string
- awsSecret string
}
tests := []struct {
name string
@@ -123,21 +121,12 @@ func TestMakeS3SessionForUploadWithBucket(t *testing.T) {
wantHelper Helper
wantErr bool
}{
- {name: "test1", args: args{awsBucket: bucket1, awsID: "AWS_ID", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: bucket1}, wantErr: false},
- {name: "test2", args: args{awsBucket: emptyBucket, awsID: "AWS_ID", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: emptyBucket}, wantErr: true},
- {name: "test3", args: args{awsBucket: bucket1, awsID: "", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: bucket1}, wantErr: true},
- {name: "test4", args: args{awsBucket: bucket1, awsID: "AWS_ID", awsSecret: ""}, wantHelper: Helper{bucket: bucket1}, wantErr: true},
- {name: "test5", args: args{awsBucket: bucket1, awsID: "", awsSecret: ""}, wantHelper: Helper{bucket: bucket1}, wantErr: true},
- // public upload bucket requires AWS credentials for uploads
- {name: "test6", args: args{awsBucket: publicUploadBucket, awsID: "AWS_ID", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: publicUploadBucket}, wantErr: false},
- {name: "test7", args: args{awsBucket: publicUploadBucket, awsID: "", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: publicUploadBucket}, wantErr: true},
- {name: "test8", args: args{awsBucket: publicUploadBucket, awsID: "AWS_ID", awsSecret: ""}, wantHelper: Helper{bucket: publicUploadBucket}, wantErr: true},
- {name: "test9", args: args{awsBucket: publicUploadBucket, awsID: "", awsSecret: ""}, wantHelper: Helper{bucket: publicUploadBucket}, wantErr: true},
+ {name: "test1", args: args{awsBucket: bucket1}, wantHelper: Helper{bucket: bucket1}, wantErr: false},
+ {name: "test2", args: args{awsBucket: emptyBucket}, wantHelper: Helper{bucket: emptyBucket}, wantErr: true},
+ {name: "test6", args: args{awsBucket: publicUploadBucket}, wantHelper: Helper{bucket: publicUploadBucket}, wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- os.Setenv("AWS_ACCESS_KEY_ID", tt.args.awsID)
- os.Setenv("AWS_SECRET_ACCESS_KEY", tt.args.awsSecret)
gotHelper, err := MakeS3SessionForUploadWithBucket(tt.args.awsBucket)
if (err != nil) != tt.wantErr {
t.Errorf("MakeS3SessionForUploadWithBucket() error = %v, wantErr %v", err, tt.wantErr)
@@ -158,8 +147,6 @@ func TestMakeS3SessionForDownloadWithBucket(t *testing.T) {
const emptyBucket = ""
type args struct {
awsBucket string
- awsID string
- awsSecret string
}
tests := []struct {
name string
@@ -167,21 +154,12 @@ func TestMakeS3SessionForDownloadWithBucket(t *testing.T) {
wantHelper Helper
wantErr bool
}{
- {name: "test1", args: args{awsBucket: bucket1, awsID: "AWS_ID", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: bucket1}, wantErr: false},
- {name: "test2", args: args{awsBucket: emptyBucket, awsID: "AWS_ID", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: emptyBucket}, wantErr: true},
- {name: "test3", args: args{awsBucket: bucket1, awsID: "", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: bucket1}, wantErr: true},
- {name: "test4", args: args{awsBucket: bucket1, awsID: "AWS_ID", awsSecret: ""}, wantHelper: Helper{bucket: bucket1}, wantErr: true},
- {name: "test5", args: args{awsBucket: bucket1, awsID: "", awsSecret: ""}, wantHelper: Helper{bucket: bucket1}, wantErr: true},
- // public release bucket does not require AWS credentials for downloads
- {name: "test6", args: args{awsBucket: publicReleaseBucket, awsID: "AWS_ID", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: publicReleaseBucket}, wantErr: false},
- {name: "test7", args: args{awsBucket: publicReleaseBucket, awsID: "", awsSecret: "AWS_SECRET"}, wantHelper: Helper{bucket: publicReleaseBucket}, wantErr: false},
- {name: "test8", args: args{awsBucket: publicReleaseBucket, awsID: "AWS_ID", awsSecret: ""}, wantHelper: Helper{bucket: publicReleaseBucket}, wantErr: false},
- {name: "test9", args: args{awsBucket: publicReleaseBucket, awsID: "", awsSecret: ""}, wantHelper: Helper{bucket: publicReleaseBucket}, wantErr: false},
+ {name: "test1", args: args{awsBucket: bucket1}, wantHelper: Helper{bucket: bucket1}, wantErr: false},
+ {name: "test2", args: args{awsBucket: emptyBucket}, wantHelper: Helper{bucket: emptyBucket}, wantErr: true},
+ {name: "test6", args: args{awsBucket: publicReleaseBucket}, wantHelper: Helper{bucket: publicReleaseBucket}, wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- os.Setenv("AWS_ACCESS_KEY_ID", tt.args.awsID)
- os.Setenv("AWS_SECRET_ACCESS_KEY", tt.args.awsSecret)
gotHelper, err := MakeS3SessionForDownloadWithBucket(tt.args.awsBucket)
if (err != nil) != tt.wantErr {
t.Errorf("MakeS3SessionForDownloadWithBucket() error = %v, wantErr %v", err, tt.wantErr)
diff --git a/util/sleep.go b/util/sleep.go
index 0d3a60acf..3ad397bf0 100644
--- a/util/sleep.go
+++ b/util/sleep.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/sleep_linux.go b/util/sleep_linux.go
index 02d450303..1540fce73 100644
--- a/util/sleep_linux.go
+++ b/util/sleep_linux.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/sleep_linux_32.go b/util/sleep_linux_32.go
index 50a0e696c..8dd445e2a 100644
--- a/util/sleep_linux_32.go
+++ b/util/sleep_linux_32.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/sleep_linux_64.go b/util/sleep_linux_64.go
index b2f7a69db..0fff615e7 100644
--- a/util/sleep_linux_64.go
+++ b/util/sleep_linux_64.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/tar/tar.go b/util/tar/tar.go
index 68d03b51f..2ffc78c1b 100644
--- a/util/tar/tar.go
+++ b/util/tar/tar.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/tar/untar.go b/util/tar/untar.go
index e6de065a6..fcbccc755 100644
--- a/util/tar/untar.go
+++ b/util/tar/untar.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/tcpinfo.go b/util/tcpinfo.go
index 2b4c69d29..c387bba33 100644
--- a/util/tcpinfo.go
+++ b/util/tcpinfo.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/tcpinfo_darwin.go b/util/tcpinfo_darwin.go
index cae19d06d..ecb06ab66 100644
--- a/util/tcpinfo_darwin.go
+++ b/util/tcpinfo_darwin.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/tcpinfo_linux.go b/util/tcpinfo_linux.go
index 3da707e1c..8cf1687ae 100644
--- a/util/tcpinfo_linux.go
+++ b/util/tcpinfo_linux.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -56,6 +56,7 @@ func getConnTCPInfo(raw syscall.RawConn) (*TCPInfo, error) {
// linuxTCPInfo is based on linux include/uapi/linux/tcp.h struct tcp_info
//revive:disable:var-naming
+//nolint:structcheck // complains about unused fields that are rqeuired to match C tcp_info struct
type linuxTCPInfo struct {
state uint8
ca_state uint8
diff --git a/util/tcpinfo_noop.go b/util/tcpinfo_noop.go
index a155ed929..7eecba758 100644
--- a/util/tcpinfo_noop.go
+++ b/util/tcpinfo_noop.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/timers/frozen.go b/util/timers/frozen.go
index a9aa6c1b7..e6487b1a8 100644
--- a/util/timers/frozen.go
+++ b/util/timers/frozen.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/timers/interface.go b/util/timers/interface.go
index 6c8c493e1..e96aced75 100644
--- a/util/timers/interface.go
+++ b/util/timers/interface.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/timers/monotonic.go b/util/timers/monotonic.go
index 80b1a7de4..70db87da3 100644
--- a/util/timers/monotonic.go
+++ b/util/timers/monotonic.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/timers/monotonic_test.go b/util/timers/monotonic_test.go
index b9fecdbdb..f8821b300 100644
--- a/util/timers/monotonic_test.go
+++ b/util/timers/monotonic_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/tokens/tokens.go b/util/tokens/tokens.go
index 930f030bb..1064a5863 100644
--- a/util/tokens/tokens.go
+++ b/util/tokens/tokens.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/util.go b/util/util.go
index a19a5b069..f3699188c 100644
--- a/util/util.go
+++ b/util/util.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -26,6 +26,16 @@ import (
/* misc */
+// GetFdLimits returns a current values for file descriptors limits.
+func GetFdLimits() (soft uint64, hard uint64, err error) {
+ var rLimit syscall.Rlimit
+ err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
+ if err != nil {
+ return 0, 0, fmt.Errorf("GetFdSoftLimit() err: %w", err)
+ }
+ return rLimit.Cur, rLimit.Max, nil
+}
+
// SetFdSoftLimit sets a new file descriptors soft limit.
func SetFdSoftLimit(newLimit uint64) error {
var rLimit syscall.Rlimit
diff --git a/util/util_windows.go b/util/util_windows.go
index 5a533b655..b485f8e25 100644
--- a/util/util_windows.go
+++ b/util/util_windows.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
@@ -18,12 +18,18 @@ package util
import (
"errors"
+ "math"
"syscall"
"time"
)
/* misc */
+// GetFdLimits returns a current values for file descriptors limits.
+func GetFdLimits() (soft uint64, hard uint64, err error) {
+ return math.MaxUint64, math.MaxUint64, nil // syscall.RLIM_INFINITY
+}
+
// SetFdSoftLimit sets a new file descriptors soft limit.
func SetFdSoftLimit(_ uint64) error {
return nil
diff --git a/util/uuid/uuid.go b/util/uuid/uuid.go
index ab99a5a3d..e159ecf46 100644
--- a/util/uuid/uuid.go
+++ b/util/uuid/uuid.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/uuid/uuid_test.go b/util/uuid/uuid_test.go
index 665667225..17914af3c 100644
--- a/util/uuid/uuid_test.go
+++ b/util/uuid/uuid_test.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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
diff --git a/util/watchdogStreamReader.go b/util/watchdogStreamReader.go
index 8842483bd..a54ca376b 100644
--- a/util/watchdogStreamReader.go
+++ b/util/watchdogStreamReader.go
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2022 Algorand, Inc.
+// 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