summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralgoidan <79864820+algoidan@users.noreply.github.com>2023-03-30 13:10:18 +0300
committerGitHub <noreply@github.com>2023-03-30 13:10:18 +0300
commitcd7dbcc7d99cdb31dba163365f7029115185d4b3 (patch)
treed0b6d309de97fc5462c6a0f93e7d559c8ff40fb6
parent8bf459bf12d2e2ad4cb5ccd4d81e3c98da9359b5 (diff)
parentfdbe4e593ac62b0cfd50a06f6f592923c3aa3f1a (diff)
Merge pull request #5241 from algoidan/merge-sp-recoverability-from-masterfeature/stateproofs-recoverability
Merge sp recoverability from master
-rw-r--r--cmd/algod/main.go13
-rw-r--r--cmd/catchpointdump/file.go25
-rw-r--r--cmd/goal/application.go2
-rw-r--r--cmd/goal/clerk.go58
-rw-r--r--cmd/updater/version_test.go9
-rw-r--r--config/config.go2
-rw-r--r--config/version.go2
-rw-r--r--crypto/merkletrie/cache.go26
-rw-r--r--crypto/merkletrie/cache_test.go4
-rw-r--r--crypto/merkletrie/committer.go2
-rw-r--r--crypto/merkletrie/committer_test.go10
-rw-r--r--crypto/merkletrie/node.go20
-rw-r--r--crypto/merkletrie/node_test.go100
-rw-r--r--crypto/merkletrie/trie.go50
-rw-r--r--daemon/algod/api/algod.oas2.json19
-rw-r--r--daemon/algod/api/algod.oas3.yml20
-rw-r--r--daemon/algod/api/client/restClient.go18
-rw-r--r--daemon/algod/api/server/v2/generated/data/routes.go324
-rw-r--r--daemon/algod/api/server/v2/generated/experimental/routes.go50
-rw-r--r--daemon/algod/api/server/v2/generated/model/types.go12
-rw-r--r--daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go60
-rw-r--r--daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go459
-rw-r--r--daemon/algod/api/server/v2/generated/participating/private/routes.go336
-rw-r--r--daemon/algod/api/server/v2/generated/participating/public/routes.go353
-rw-r--r--daemon/algod/api/server/v2/handlers.go14
-rw-r--r--daemon/algod/api/server/v2/test/handlers_test.go20
-rw-r--r--daemon/algod/api/server/v2/utils.go12
-rw-r--r--daemon/algod/server.go12
-rw-r--r--data/transactions/logic/mocktracer/scenarios.go24
-rw-r--r--data/txHandler.go41
-rw-r--r--data/txHandler_test.go62
-rw-r--r--docker/README.md22
-rw-r--r--docker/files/run/followermode_template.json51
-rwxr-xr-xdocker/files/run/run.sh21
-rw-r--r--ledger/acctupdates_test.go4
-rw-r--r--ledger/archival_test.go4
-rw-r--r--ledger/catchpointtracker_test.go1
-rw-r--r--ledger/double_test.go10
-rw-r--r--ledger/eval/appcow.go (renamed from ledger/internal/appcow.go)5
-rw-r--r--ledger/eval/appcow_test.go (renamed from ledger/internal/appcow_test.go)2
-rw-r--r--ledger/eval/applications.go (renamed from ledger/internal/applications.go)2
-rw-r--r--ledger/eval/assetcow.go (renamed from ledger/internal/assetcow.go)2
-rw-r--r--ledger/eval/cow.go (renamed from ledger/internal/cow.go)2
-rw-r--r--ledger/eval/cow_creatables.go (renamed from ledger/internal/cow_creatables.go)2
-rw-r--r--ledger/eval/cow_test.go (renamed from ledger/internal/cow_test.go)2
-rw-r--r--ledger/eval/eval.go (renamed from ledger/internal/eval.go)4
-rw-r--r--ledger/eval/eval_test.go (renamed from ledger/internal/eval_test.go)2
-rw-r--r--ledger/eval/evalindexer.go (renamed from ledger/internal/evalindexer.go)2
-rw-r--r--ledger/eval/prefetcher/error.go (renamed from ledger/internal/prefetcher/error.go)0
-rw-r--r--ledger/eval/prefetcher/prefetcher.go (renamed from ledger/internal/prefetcher/prefetcher.go)0
-rw-r--r--ledger/eval/prefetcher/prefetcher_alignment_test.go (renamed from ledger/internal/prefetcher/prefetcher_alignment_test.go)6
-rw-r--r--ledger/eval/prefetcher/prefetcher_test.go (renamed from ledger/internal/prefetcher/prefetcher_test.go)2
-rw-r--r--ledger/eval/prefetcher/prefetcher_whitebox_test.go (renamed from ledger/internal/prefetcher/prefetcher_whitebox_test.go)0
-rw-r--r--ledger/evalbench_test.go4
-rw-r--r--ledger/evalindexer.go6
-rw-r--r--ledger/fullblock_perf_test.go12
-rw-r--r--ledger/ledger.go20
-rw-r--r--ledger/ledger_perf_test.go4
-rw-r--r--ledger/ledger_test.go4
-rw-r--r--ledger/simple_test.go16
-rw-r--r--ledger/simulation/simulation_eval_test.go308
-rw-r--r--ledger/simulation/simulator.go7
-rw-r--r--ledger/simulation/simulator_test.go4
-rw-r--r--ledger/simulation/trace.go15
-rw-r--r--ledger/simulation/tracer.go17
-rw-r--r--ledger/store/merkle_committer.go75
-rw-r--r--ledger/store/trackerdb/sqlitedriver/accountsV2.go7
-rw-r--r--ledger/tracker.go4
-rw-r--r--libgoal/libgoal.go19
-rw-r--r--network/wsNetwork.go9
-rw-r--r--network/wsNetwork_test.go36
-rw-r--r--scripts/algorand_node_log.json58
-rwxr-xr-xscripts/configure_dev.sh2
-rwxr-xr-xtest/scripts/e2e_subs/e2e-app-simulate.sh139
-rw-r--r--test/testdata/deployednettemplates/recipes/custom/configs/nonPartNode.json2
-rw-r--r--test/testdata/deployednettemplates/recipes/custom/configs/relay.json2
-rw-r--r--tools/debug/chopper/main.go230
-rwxr-xr-xtools/debug/jslog2
-rw-r--r--util/s3/s3Helper.go22
-rw-r--r--util/s3/s3Helper_test.go73
80 files changed, 2296 insertions, 1106 deletions
diff --git a/cmd/algod/main.go b/cmd/algod/main.go
index cb0960e61..8ff24a970 100644
--- a/cmd/algod/main.go
+++ b/cmd/algod/main.go
@@ -284,10 +284,15 @@ func run() int {
if err != nil {
log.Errorf("cannot locate node executable: %s", err)
} else {
- phonebookDir := filepath.Dir(ex)
- phonebookAddresses, err = config.LoadPhonebook(phonebookDir)
- if err != nil {
- log.Debugf("Cannot load static phonebook: %v", err)
+ phonebookDirs := []string{filepath.Dir(ex), dataDir}
+ for _, phonebookDir := range phonebookDirs {
+ phonebookAddresses, err = config.LoadPhonebook(phonebookDir)
+ if err == nil {
+ log.Debugf("Static phonebook loaded from %s", phonebookDir)
+ break
+ } else {
+ log.Debugf("Cannot load static phonebook from %s dir: %v", phonebookDir, err)
+ }
}
}
}
diff --git a/cmd/catchpointdump/file.go b/cmd/catchpointdump/file.go
index a75dbe8e4..e99a0b406 100644
--- a/cmd/catchpointdump/file.go
+++ b/cmd/catchpointdump/file.go
@@ -170,31 +170,36 @@ func isGzipCompressed(catchpointReader *bufio.Reader, catchpointFileSize int64)
return prefixBytes[0] == gzipPrefix[0] && prefixBytes[1] == gzipPrefix[1]
}
-func getCatchpointTarReader(catchpointReader *bufio.Reader, catchpointFileSize int64) (*tar.Reader, error) {
+func getCatchpointTarReader(catchpointReader *bufio.Reader, catchpointFileSize int64) (*tar.Reader, bool, error) {
if isGzipCompressed(catchpointReader, catchpointFileSize) {
gzipReader, err := gzip.NewReader(catchpointReader)
if err != nil {
- return nil, err
+ return nil, false, err
}
-
- return tar.NewReader(gzipReader), nil
+ return tar.NewReader(gzipReader), true, nil
}
- return tar.NewReader(catchpointReader), nil
+ return tar.NewReader(catchpointReader), false, nil
}
func loadCatchpointIntoDatabase(ctx context.Context, catchupAccessor ledger.CatchpointCatchupAccessor, catchpointFile io.Reader, catchpointFileSize int64) (fileHeader ledger.CatchpointFileHeader, err error) {
fmt.Printf("\n")
- printLoadCatchpointProgressLine(0, 50, 0)
+ const barLength = 50
+ printLoadCatchpointProgressLine(0, barLength, 0)
lastProgressUpdate := time.Now()
progress := uint64(0)
defer printLoadCatchpointProgressLine(0, 0, 0)
catchpointReader := bufio.NewReader(catchpointFile)
- tarReader, err := getCatchpointTarReader(catchpointReader, catchpointFileSize)
+ tarReader, isCompressed, err := getCatchpointTarReader(catchpointReader, catchpointFileSize)
if err != nil {
return fileHeader, err
}
+ if isCompressed {
+ // gzip'ed file is about 3-6 times smaller than tar
+ // modify catchpointFileSize to make the progress bar more-less reflecting the state
+ catchpointFileSize = 4 * catchpointFileSize
+ }
var downloadProgress ledger.CatchpointCatchupAccessorProgress
for {
@@ -232,7 +237,11 @@ func loadCatchpointIntoDatabase(ctx context.Context, catchupAccessor ledger.Catc
}
if time.Since(lastProgressUpdate) > 50*time.Millisecond && catchpointFileSize > 0 {
lastProgressUpdate = time.Now()
- printLoadCatchpointProgressLine(int(float64(progress)*50.0/float64(catchpointFileSize)), 50, int64(progress))
+ progressRatio := int(float64(progress) * barLength / float64(catchpointFileSize))
+ if progressRatio > barLength {
+ progressRatio = barLength
+ }
+ printLoadCatchpointProgressLine(progressRatio, barLength, int64(progress))
}
}
}
diff --git a/cmd/goal/application.go b/cmd/goal/application.go
index 092079330..0c5e9e0cb 100644
--- a/cmd/goal/application.go
+++ b/cmd/goal/application.go
@@ -1063,7 +1063,7 @@ var infoAppCmd = &cobra.Command{
}
// populateMethodCallTxnArgs parses and loads transactions from the files indicated by the values
-// slice. An error will occur if the transaction does not matched the expected type, it has a nonzero
+// slice. An error will occur if the transaction does not match the expected type, it has a nonzero
// group ID, or if it is signed by a normal signature or Msig signature (but not Lsig signature)
func populateMethodCallTxnArgs(types []string, values []string) ([]transactions.SignedTxn, error) {
loadedTxns := make([]transactions.SignedTxn, len(values))
diff --git a/cmd/goal/clerk.go b/cmd/goal/clerk.go
index 214782894..f044915fb 100644
--- a/cmd/goal/clerk.go
+++ b/cmd/goal/clerk.go
@@ -76,6 +76,7 @@ func init() {
clerkCmd.AddCommand(compileCmd)
clerkCmd.AddCommand(dryrunCmd)
clerkCmd.AddCommand(dryrunRemoteCmd)
+ clerkCmd.AddCommand(simulateCmd)
// Wallet to be used for the clerk operation
clerkCmd.PersistentFlags().StringVarP(&walletName, "wallet", "w", "", "Set the wallet to be used for the selected operation")
@@ -88,7 +89,7 @@ func init() {
sendCmd.Flags().StringVar(&rekeyToAddress, "rekey-to", "", "Rekey account to the given spending key/address. (Future transactions from this account will need to be signed with the new key.)")
sendCmd.Flags().StringVarP(&programSource, "from-program", "F", "", "Program source to use as account logic")
sendCmd.Flags().StringVarP(&progByteFile, "from-program-bytes", "P", "", "Program binary to use as account logic")
- sendCmd.Flags().StringSliceVar(&argB64Strings, "argb64", nil, "base64 encoded args to pass to transaction logic")
+ sendCmd.Flags().StringSliceVar(&argB64Strings, "argb64", nil, "Base64 encoded args to pass to transaction logic")
sendCmd.Flags().StringVarP(&logicSigFile, "logic-sig", "L", "", "LogicSig to apply to transaction")
sendCmd.Flags().StringVar(&msigParams, "msig-params", "", "Multisig preimage parameters - [threshold] [Address 1] [Address 2] ...\nUsed to add the necessary fields in case the account was rekeyed to a multisig account")
sendCmd.MarkFlagRequired("to")
@@ -108,8 +109,8 @@ func init() {
signCmd.Flags().StringVarP(&signerAddress, "signer", "S", "", "Address of key to sign with, if different from transaction \"from\" address due to rekeying")
signCmd.Flags().StringVarP(&programSource, "program", "p", "", "Program source to use as account logic")
signCmd.Flags().StringVarP(&logicSigFile, "logic-sig", "L", "", "LogicSig to apply to transaction")
- signCmd.Flags().StringSliceVar(&argB64Strings, "argb64", nil, "base64 encoded args to pass to transaction logic")
- signCmd.Flags().StringVarP(&protoVersion, "proto", "P", "", "consensus protocol version id string")
+ signCmd.Flags().StringSliceVar(&argB64Strings, "argb64", nil, "Base64 encoded args to pass to transaction logic")
+ signCmd.Flags().StringVarP(&protoVersion, "proto", "P", "", "Consensus protocol version id string")
signCmd.MarkFlagRequired("infile")
signCmd.MarkFlagRequired("outfile")
@@ -123,26 +124,29 @@ func init() {
splitCmd.MarkFlagRequired("infile")
splitCmd.MarkFlagRequired("outfile")
- compileCmd.Flags().BoolVarP(&disassemble, "disassemble", "D", false, "disassemble a compiled program")
- compileCmd.Flags().BoolVarP(&noProgramOutput, "no-out", "n", false, "don't write contract program binary")
- compileCmd.Flags().BoolVarP(&writeSourceMap, "map", "m", false, "write out source map")
- compileCmd.Flags().BoolVarP(&signProgram, "sign", "s", false, "sign program, output is a binary signed LogicSig record")
+ compileCmd.Flags().BoolVarP(&disassemble, "disassemble", "D", false, "Disassemble a compiled program")
+ compileCmd.Flags().BoolVarP(&noProgramOutput, "no-out", "n", false, "Don't write contract program binary")
+ compileCmd.Flags().BoolVarP(&writeSourceMap, "map", "m", false, "Write out source map")
+ compileCmd.Flags().BoolVarP(&signProgram, "sign", "s", false, "Sign program, output is a binary signed LogicSig record")
compileCmd.Flags().StringVarP(&outFilename, "outfile", "o", "", "Filename to write program bytes or signed LogicSig to")
compileCmd.Flags().StringVarP(&account, "account", "a", "", "Account address to sign the program (If not specified, uses default account)")
- dryrunCmd.Flags().StringVarP(&txFilename, "txfile", "t", "", "transaction or transaction-group to test")
- dryrunCmd.Flags().StringVarP(&protoVersion, "proto", "P", "", "consensus protocol version id string")
+ dryrunCmd.Flags().StringVarP(&txFilename, "txfile", "t", "", "Transaction or transaction-group to test")
+ dryrunCmd.Flags().StringVarP(&protoVersion, "proto", "P", "", "Consensus protocol version id string")
dryrunCmd.Flags().BoolVar(&dumpForDryrun, "dryrun-dump", false, "Dump in dryrun format acceptable by dryrun REST api instead of running")
dryrunCmd.Flags().Var(&dumpForDryrunFormat, "dryrun-dump-format", "Dryrun dump format: "+dumpForDryrunFormat.AllowedString())
- dryrunCmd.Flags().StringSliceVar(&dumpForDryrunAccts, "dryrun-accounts", nil, "additional accounts to include into dryrun request obj")
+ dryrunCmd.Flags().StringSliceVar(&dumpForDryrunAccts, "dryrun-accounts", nil, "Additional accounts to include into dryrun request obj")
dryrunCmd.Flags().StringVarP(&outFilename, "outfile", "o", "", "Filename for writing dryrun state object")
dryrunCmd.MarkFlagRequired("txfile")
- dryrunRemoteCmd.Flags().StringVarP(&txFilename, "dryrun-state", "D", "", "dryrun request object to run")
- dryrunRemoteCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "print more info")
- dryrunRemoteCmd.Flags().BoolVarP(&rawOutput, "raw", "r", false, "output raw response from algod")
+ dryrunRemoteCmd.Flags().StringVarP(&txFilename, "dryrun-state", "D", "", "Dryrun request object to run")
+ dryrunRemoteCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Print more info")
+ dryrunRemoteCmd.Flags().BoolVarP(&rawOutput, "raw", "r", false, "Output raw response from algod")
dryrunRemoteCmd.MarkFlagRequired("dryrun-state")
+ simulateCmd.Flags().StringVarP(&txFilename, "txfile", "t", "", "Transaction or transaction-group to test")
+ simulateCmd.Flags().StringVarP(&outFilename, "outfile", "o", "", "Filename for writing simulation result")
+ panicIfErr(simulateCmd.MarkFlagRequired("txfile"))
}
var clerkCmd = &cobra.Command{
@@ -1253,6 +1257,34 @@ var dryrunRemoteCmd = &cobra.Command{
},
}
+var simulateCmd = &cobra.Command{
+ Use: "simulate",
+ Short: "Simulate a transaction or transaction group with algod's simulate REST endpoint",
+ Long: `Simulate a transaction or transaction group with algod's simulate REST endpoint under various configurations.`,
+ Run: func(cmd *cobra.Command, args []string) {
+ data, err := readFile(txFilename)
+ if err != nil {
+ reportErrorf(fileReadError, txFilename, err)
+ }
+
+ dataDir := datadir.EnsureSingleDataDir()
+ client := ensureFullClient(dataDir)
+ resp, err := client.TransactionSimulation(data)
+ if err != nil {
+ reportErrorf("simulation error: %s", err.Error())
+ }
+
+ if outFilename != "" {
+ err = writeFile(outFilename, protocol.EncodeJSON(&resp), 0600)
+ if err != nil {
+ reportErrorf("write file error: %s", err.Error())
+ }
+ } else {
+ fmt.Println(string(protocol.EncodeJSON(&resp)))
+ }
+ },
+}
+
// unmarshalSlice converts string addresses to basics.Address
func unmarshalSlice(accts []string) ([]basics.Address, error) {
result := make([]basics.Address, 0, len(accts))
diff --git a/cmd/updater/version_test.go b/cmd/updater/version_test.go
index 62954d51c..e46bd2f3e 100644
--- a/cmd/updater/version_test.go
+++ b/cmd/updater/version_test.go
@@ -29,10 +29,11 @@ func TestGetVersion(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
- testValidVersion(t, "algonode_update_0.1.0.log", uint64(0x00010000))
- testValidVersion(t, "algo_update_0.1.0", uint64(0x00010000))
- testValidVersion(t, "algo_update_65535.1.0", uint64(0xFFFF00010000))
- testValidVersion(t, "algo_update_65535.65535.65535", uint64(0xFFFFFFFFFFFF))
+ testValidVersion(t, "algonode_update_0.1.0.log", uint64(0x01000000))
+ testValidVersion(t, "algo_update_0.1.0", uint64(0x01000000))
+ testValidVersion(t, "algo_update_65535.1.0", uint64(0x00FFFF0001000000))
+ testValidVersion(t, "algo_update_65535.65535.65535", uint64(0xFFFFFFFF00FFFF))
+ testValidVersion(t, "algo_update_65535.65535.16777215", uint64(0xFFFFFFFFFFFFFF))
testInvalidVersion(t, "algo_update_0.-1.0")
testInvalidVersion(t, "algo_update_1e5.0.0")
diff --git a/config/config.go b/config/config.go
index a180a9a47..1da9dd059 100644
--- a/config/config.go
+++ b/config/config.go
@@ -72,7 +72,7 @@ const StateProofFileName = "stateproof.sqlite"
// It is used for tracking participation key metadata.
const ParticipationRegistryFilename = "partregistry.sqlite"
-// ConfigurableConsensusProtocolsFilename defines a set of consensus prototocols that
+// ConfigurableConsensusProtocolsFilename defines a set of consensus protocols that
// are to be loaded from the data directory ( if present ), to override the
// built-in supported consensus protocols.
const ConfigurableConsensusProtocolsFilename = "consensus.json"
diff --git a/config/version.go b/config/version.go
index 06701e9f1..e4c5f57df 100644
--- a/config/version.go
+++ b/config/version.go
@@ -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 = 15
+const VersionMinor = 16
// Version is the type holding our full version information.
type Version struct {
diff --git a/crypto/merkletrie/cache.go b/crypto/merkletrie/cache.go
index 208397edb..01648d73a 100644
--- a/crypto/merkletrie/cache.go
+++ b/crypto/merkletrie/cache.go
@@ -39,13 +39,13 @@ const (
// be found in neither the in-memory cache or on the persistent storage.
var ErrLoadedPageMissingNode = errors.New("loaded page is missing a node")
-// ErrPageDecodingFailuire is returned if the decoding of a page has failed.
-var ErrPageDecodingFailuire = errors.New("error encountered while decoding page")
+// ErrPageDecodingFailure is returned if the decoding of a page has failed.
+var ErrPageDecodingFailure = errors.New("error encountered while decoding page")
type merkleTrieCache struct {
- // mt is a point to the originating trie
+ // mt is a pointer to the originating trie
mt *Trie
- // committer is the backing up storage for the cache. ( memory, database, etc. )
+ // committer is the backing store for the cache. ( memory, database, etc. )
committer Committer
// cachedNodeCount is the number of currently cached, in-memory, nodes stored in the pageToNIDsPtr structure.
cachedNodeCount int
@@ -293,7 +293,7 @@ func (mtc *merkleTrieCache) beginTransaction() {
mtc.txNextNodeID = mtc.mt.nextNodeID
}
-// commitTransaction - used internaly by the Trie
+// commitTransaction - used internally by the Trie
func (mtc *merkleTrieCache) commitTransaction() {
// the created nodes are already on the list.
for nodeID := range mtc.txCreatedNodeIDs {
@@ -426,7 +426,7 @@ func (mtc *merkleTrieCache) commit() (CommitStats, error) {
// reallocatePendingPages is called by the commit() function, and is responsible for performing two tasks -
// 1. calculate the hashes of all the newly created nodes
-// 2. reornigize the pending flush nodes into an optimal page list, and construct a list of pages that need to be created, deleted and updated.
+// 2. reorganize the pending flush nodes into an optimal page list, and construct a list of pages that need to be created, deleted and updated.
func (mtc *merkleTrieCache) reallocatePendingPages(stats *CommitStats) (pagesToCreate []uint64, pagesToDelete map[uint64]bool, pagesToUpdate map[uint64]map[storedNodeIdentifier]*node, err error) {
// newPageThreshold is the threshold at which all the pages are newly created pages that were never committed.
newPageThreshold := uint64(mtc.mt.lastCommittedNodeID) / uint64(mtc.nodesPerPage)
@@ -669,26 +669,26 @@ func (mtc *merkleTrieCache) reallocateNode(nid storedNodeIdentifier) storedNodeI
func decodePage(bytes []byte) (nodesMap map[storedNodeIdentifier]*node, err error) {
version, versionLength := binary.Uvarint(bytes[:])
if versionLength <= 0 {
- return nil, ErrPageDecodingFailuire
+ return nil, ErrPageDecodingFailure
}
- if version != NodePageVersion {
- return nil, ErrPageDecodingFailuire
+ if version != nodePageVersion {
+ return nil, ErrPageDecodingFailure
}
nodesCount, nodesCountLength := binary.Varint(bytes[versionLength:])
if nodesCountLength <= 0 {
- return nil, ErrPageDecodingFailuire
+ return nil, ErrPageDecodingFailure
}
nodesMap = make(map[storedNodeIdentifier]*node)
walk := nodesCountLength + versionLength
for i := int64(0); i < nodesCount; i++ {
nodeID, nodesIDLength := binary.Uvarint(bytes[walk:])
if nodesIDLength <= 0 {
- return nil, ErrPageDecodingFailuire
+ return nil, ErrPageDecodingFailure
}
walk += nodesIDLength
pnode, nodeLength := deserializeNode(bytes[walk:])
if nodeLength <= 0 {
- return nil, ErrPageDecodingFailuire
+ return nil, ErrPageDecodingFailure
}
walk += nodeLength
nodesMap[storedNodeIdentifier(nodeID)] = pnode
@@ -699,7 +699,7 @@ func decodePage(bytes []byte) (nodesMap map[storedNodeIdentifier]*node, err erro
// decodePage encodes a page contents into a byte array
func (mtc *merkleTrieCache) encodePage(nodeIDs map[storedNodeIdentifier]*node, serializedBuffer []byte) []byte {
- version := binary.PutUvarint(serializedBuffer[:], NodePageVersion)
+ version := binary.PutUvarint(serializedBuffer[:], nodePageVersion)
length := binary.PutVarint(serializedBuffer[version:], int64(len(nodeIDs)))
walk := version + length
for nodeID, pnode := range nodeIDs {
diff --git a/crypto/merkletrie/cache_test.go b/crypto/merkletrie/cache_test.go
index d9c7a23e3..5967f0114 100644
--- a/crypto/merkletrie/cache_test.go
+++ b/crypto/merkletrie/cache_test.go
@@ -298,7 +298,7 @@ func (mt *Trie) TestDeleteRollback(d []byte) (bool, error) {
if err != nil {
return false, err
}
- found, err := pnode.find(mt.cache, d[:])
+ found, err := pnode.find(&mt.cache, d[:])
if !found || err != nil {
return false, err
}
@@ -311,7 +311,7 @@ func (mt *Trie) TestDeleteRollback(d []byte) (bool, error) {
mt.elementLength = 0
return true, nil
}
- _, err = pnode.remove(mt.cache, d[:], make([]byte, 0, len(d)))
+ _, err = pnode.remove(&mt.cache, d[:], make([]byte, 0, len(d)))
// unlike the "real" function, we want always to fail here to test the rollbackTransaction() functionality.
mt.cache.rollbackTransaction()
return false, fmt.Errorf("this is a test for failing a Delete request")
diff --git a/crypto/merkletrie/committer.go b/crypto/merkletrie/committer.go
index bad5fe7e0..5e5ae758a 100644
--- a/crypto/merkletrie/committer.go
+++ b/crypto/merkletrie/committer.go
@@ -26,7 +26,7 @@ const (
inMemoryCommitterPageSize = int64(512)
)
-// InMemoryCommitter is a fully function in-memory committer, supporting
+// InMemoryCommitter is a fully functional in-memory committer, supporting
// persistence of pages.
type InMemoryCommitter struct {
memStore map[uint64][]byte
diff --git a/crypto/merkletrie/committer_test.go b/crypto/merkletrie/committer_test.go
index 6f8dfb2a7..c8bfd7143 100644
--- a/crypto/merkletrie/committer_test.go
+++ b/crypto/merkletrie/committer_test.go
@@ -140,18 +140,18 @@ func TestNoRedundentPages(t *testing.T) {
require.Equal(t, nodesCount, mt1.cache.cachedNodeCount)
}
-// decodePage decodes a byte array into a page content
+// decodePageHeaderSize decodes a page header at the start of a byte array
func decodePageHeaderSize(bytes []byte) (headerSize int, err error) {
version, versionLength := binary.Uvarint(bytes[:])
if versionLength <= 0 {
- return 0, ErrPageDecodingFailuire
+ return 0, ErrPageDecodingFailure
}
- if version != NodePageVersion {
- return 0, ErrPageDecodingFailuire
+ if version != nodePageVersion {
+ return 0, ErrPageDecodingFailure
}
_, nodesCountLength := binary.Varint(bytes[versionLength:])
if nodesCountLength <= 0 {
- return 0, ErrPageDecodingFailuire
+ return 0, ErrPageDecodingFailure
}
return nodesCountLength + versionLength, nil
}
diff --git a/crypto/merkletrie/node.go b/crypto/merkletrie/node.go
index 33c2f673a..de765793d 100644
--- a/crypto/merkletrie/node.go
+++ b/crypto/merkletrie/node.go
@@ -140,8 +140,9 @@ func (n *node) add(cache *merkleTrieCache, d []byte, path []byte) (nodeID stored
}
pnode.hash = append(path, d[:idiff]...)
+ // create ancestors from pnode up to the new split
for i := idiff - 1; i >= 0; i-- {
- // create a parent node for pnode.
+ // create a parent node for pnode, and move up
pnode2, nodeID2 := cache.allocateNewNode()
pnode2.childrenMask.SetBit(d[i])
pnode2.children = []childEntry{
@@ -152,7 +153,6 @@ func (n *node) add(cache *merkleTrieCache, d []byte, path []byte) (nodeID stored
}
pnode2.hash = append(path, d[:i]...)
- pnode = pnode2
nodeID = nodeID2
}
return nodeID, nil
@@ -160,16 +160,14 @@ func (n *node) add(cache *merkleTrieCache, d []byte, path []byte) (nodeID stored
if n.childrenMask.Bit(d[0]) == false {
// no such child.
- var childNode *node
- var childNodeID storedNodeIdentifier
- childNode, childNodeID = cache.allocateNewNode()
+ childNode, childNodeID := cache.allocateNewNode()
childNode.hash = d[1:]
pnode, nodeID = cache.allocateNewNode()
pnode.childrenMask = n.childrenMask
pnode.childrenMask.SetBit(d[0])
- pnode.children = make([]childEntry, len(n.children)+1, len(n.children)+1)
+ pnode.children = make([]childEntry, len(n.children)+1)
if d[0] > n.children[len(n.children)-1].hashIndex {
// the new entry comes after all the existing ones.
for i, child := range n.children {
@@ -183,8 +181,8 @@ func (n *node) add(cache *merkleTrieCache, d []byte, path []byte) (nodeID stored
for i, child := range n.children {
if d[0] < child.hashIndex {
pnode.children[i] = childEntry{
- hashIndex: d[0],
id: childNodeID,
+ hashIndex: d[0],
}
// copy the rest of the items.
for ; i < len(n.children); i++ {
@@ -211,7 +209,7 @@ func (n *node) add(cache *merkleTrieCache, d []byte, path []byte) (nodeID stored
pnode, nodeID = childNode, cache.refurbishNode(curNodeID)
pnode.childrenMask = n.childrenMask
if len(pnode.children) < len(n.children) {
- pnode.children = make([]childEntry, len(n.children), len(n.children))
+ pnode.children = make([]childEntry, len(n.children))
} else {
pnode.children = pnode.children[:len(n.children)]
}
@@ -270,7 +268,7 @@ func (n *node) remove(cache *merkleTrieCache, key []byte, path []byte) (nodeID s
pnode, nodeID = childNode, cache.refurbishNode(childNodeID)
pnode.childrenMask = n.childrenMask
// we are guaranteed to have other children, because our tree forbids nodes that have exactly one leaf child and no other children.
- pnode.children = make([]childEntry, len(n.children)-1, len(n.children)-1)
+ pnode.children = make([]childEntry, len(n.children)-1)
copy(pnode.children, append(n.children[:childIndex], n.children[childIndex+1:]...))
pnode.childrenMask.ClearBit(key[0])
} else {
@@ -283,7 +281,7 @@ func (n *node) remove(cache *merkleTrieCache, key []byte, path []byte) (nodeID s
pnode, nodeID = childNode, cache.refurbishNode(childNodeID)
pnode.childrenMask = n.childrenMask
if len(pnode.children) < len(n.children) {
- pnode.children = make([]childEntry, len(n.children), len(n.children))
+ pnode.children = make([]childEntry, len(n.children))
} else {
pnode.children = pnode.children[:len(n.children)]
}
@@ -371,7 +369,7 @@ func deserializeNode(buf []byte) (n *node, s int) {
prevChildIndex = childIndex
i++
}
- n.children = make([]childEntry, i, i)
+ n.children = make([]childEntry, i)
copy(n.children, childEntries[:i])
return
}
diff --git a/crypto/merkletrie/node_test.go b/crypto/merkletrie/node_test.go
index 1495a8e9c..893fbc86b 100644
--- a/crypto/merkletrie/node_test.go
+++ b/crypto/merkletrie/node_test.go
@@ -17,6 +17,8 @@
package merkletrie
import (
+ "crypto/sha512"
+ "encoding/binary"
"testing"
"github.com/stretchr/testify/require"
@@ -67,17 +69,24 @@ func (n *node) leafUsingChildrenLength() bool {
return len(n.children) == 0
}
+func makeHashes(n int) [][]byte {
+ hashes := make([][]byte, n)
+ for i := 0; i < len(hashes); i++ {
+ buf := make([]byte, 32)
+ binary.BigEndian.PutUint64(buf, uint64(i))
+ h := crypto.Hash(buf)
+ hashes[i] = h[:]
+ }
+ return hashes
+}
+
func BenchmarkNodeLeafImplementation(b *testing.B) {
+ hashes := makeHashes(100000)
+
b.Run("leaf-ChildrenMask", func(b *testing.B) {
var memoryCommitter InMemoryCommitter
memConfig := defaultTestMemoryConfig
mt1, _ := MakeTrie(&memoryCommitter, memConfig)
- // create 100000 hashes.
- leafsCount := 100000
- hashes := make([]crypto.Digest, leafsCount)
- for i := 0; i < len(hashes); i++ {
- hashes[i] = crypto.Hash([]byte{byte(i % 256), byte((i / 256) % 256), byte(i / 65536)})
- }
for i := 0; i < len(hashes); i++ {
mt1.Add(hashes[i][:])
@@ -100,12 +109,6 @@ func BenchmarkNodeLeafImplementation(b *testing.B) {
var memoryCommitter InMemoryCommitter
memConfig := defaultTestMemoryConfig
mt1, _ := MakeTrie(&memoryCommitter, memConfig)
- // create 100000 hashes.
- leafsCount := 100000
- hashes := make([]crypto.Digest, leafsCount)
- for i := 0; i < len(hashes); i++ {
- hashes[i] = crypto.Hash([]byte{byte(i % 256), byte((i / 256) % 256), byte(i / 65536)})
- }
for i := 0; i < len(hashes); i++ {
mt1.Add(hashes[i][:])
@@ -125,3 +128,76 @@ func BenchmarkNodeLeafImplementation(b *testing.B) {
}
})
}
+
+// calculateHashIncrementally uses the Writer interface to the crypto digest to
+// avoid accumulating in a buffer. Yet it's slower! I don't know why, but
+// leaving it here to benchmark more carefully later. (The final use of
+// d.Sum(nil) instead of d.Sum(n.hash[:0]) is needed because we share the
+// backing array for the slices in node hashes. But that is not the cause of the
+// slow down.)
+func (n *node) calculateHashIncrementally(cache *merkleTrieCache) error {
+ if n.leaf() {
+ return nil
+ }
+ path := n.hash
+
+ d := sha512.New512_256()
+
+ // we add this string length before the actual string so it could get "decoded"; in practice, it makes a good domain separator.
+ d.Write([]byte{byte(len(path))})
+ d.Write(path)
+ for _, child := range n.children {
+ childNode, err := cache.getNode(child.id)
+ if err != nil {
+ return err
+ }
+ if childNode.leaf() {
+ d.Write([]byte{0})
+ } else {
+ d.Write([]byte{1})
+ }
+ // we add this string length before the actual string so it could get "decoded"; in practice, it makes a good domain separator.
+ d.Write([]byte{byte(len(childNode.hash))})
+ d.Write([]byte{child.hashIndex}) // adding the first byte of the child
+ d.Write(childNode.hash) // adding the reminder of the child
+ }
+ n.hash = d.Sum(nil)
+ return nil
+}
+
+func BenchmarkAdd(b *testing.B) {
+ b.ReportAllocs()
+
+ memConfig := defaultTestMemoryConfig
+ mt, _ := MakeTrie(&InMemoryCommitter{}, memConfig)
+ hashes := makeHashes(b.N)
+
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ mt.Add(hashes[i])
+ if i%1000 == 999 {
+ mt.Commit() // not sure how often we should Commit for a nice benchmark
+ }
+ }
+}
+
+func BenchmarkDelete(b *testing.B) {
+ b.ReportAllocs()
+
+ memConfig := defaultTestMemoryConfig
+ mt, _ := MakeTrie(&InMemoryCommitter{}, memConfig)
+ hashes := makeHashes(b.N)
+ for i := 0; i < b.N; i++ {
+ mt.Add(hashes[i])
+ }
+
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ mt.Delete(hashes[i])
+ if i%1000 == 999 { // not sure how often we should Commit for a nice benchmark
+ mt.Commit()
+ }
+ }
+}
diff --git a/crypto/merkletrie/trie.go b/crypto/merkletrie/trie.go
index 7a214e709..39051699f 100644
--- a/crypto/merkletrie/trie.go
+++ b/crypto/merkletrie/trie.go
@@ -24,16 +24,16 @@ import (
)
const (
- // MerkleTreeVersion is the version of the encoded trie. If we ever want to make changes and want to have upgrade path,
+ // merkleTreeVersion is the version of the encoded trie. If we ever want to make changes and want to have upgrade path,
// this would give us the ability to do so.
- MerkleTreeVersion = uint64(0x1000000010000000)
- // NodePageVersion is the version of the encoded node. If we ever want to make changes and want to have upgrade path,
+ merkleTreeVersion = uint64(0x1000000010000000)
+ // nodePageVersion is the version of the encoded node. If we ever want to make changes and want to have upgrade path,
// this would give us the ability to do so.
- NodePageVersion = uint64(0x1000000010000000)
+ nodePageVersion = uint64(0x1000000010000000)
)
-// ErrRootPageDecodingFailuire is returned if the decoding the root page has failed.
-var ErrRootPageDecodingFailuire = errors.New("error encountered while decoding root page")
+// ErrRootPageDecodingFailure is returned if the decoding the root page has failed.
+var ErrRootPageDecodingFailure = errors.New("error encountered while decoding root page")
// ErrMismatchingElementLength is returned when an element is being added/removed from the trie that doesn't align with the trie's previous elements length
var ErrMismatchingElementLength = errors.New("mismatching element length")
@@ -63,7 +63,7 @@ type Trie struct {
root storedNodeIdentifier
nextNodeID storedNodeIdentifier
lastCommittedNodeID storedNodeIdentifier
- cache *merkleTrieCache
+ cache merkleTrieCache
elementLength int
}
@@ -79,7 +79,7 @@ type Stats struct {
func MakeTrie(committer Committer, memoryConfig MemoryConfig) (*Trie, error) {
mt := &Trie{
root: storedNodeIdentifierNull,
- cache: &merkleTrieCache{},
+ cache: merkleTrieCache{},
nextNodeID: storedNodeIdentifierBase,
lastCommittedNodeID: storedNodeIdentifierBase,
}
@@ -106,11 +106,9 @@ func MakeTrie(committer Committer, memoryConfig MemoryConfig) (*Trie, error) {
return mt, nil
}
-// SetCommitter set the provided committter as the current committer, and return the old one.
-func (mt *Trie) SetCommitter(committer Committer) (prevCommitter Committer) {
- prevCommitter = mt.cache.committer
+// SetCommitter sets the provided committer as the current committer
+func (mt *Trie) SetCommitter(committer Committer) {
mt.cache.committer = committer
- return
}
// RootHash returns the root hash of all the elements in the trie
@@ -154,13 +152,13 @@ func (mt *Trie) Add(d []byte) (bool, error) {
if err != nil {
return false, err
}
- found, err := pnode.find(mt.cache, d[:])
+ found, err := pnode.find(&mt.cache, d[:])
if found || (err != nil) {
return false, err
}
mt.cache.beginTransaction()
var updatedRoot storedNodeIdentifier
- updatedRoot, err = pnode.add(mt.cache, d[:], make([]byte, 0, len(d)))
+ updatedRoot, err = pnode.add(&mt.cache, d[:], make([]byte, 0, len(d)))
if err != nil {
mt.cache.rollbackTransaction()
return false, err
@@ -184,7 +182,7 @@ func (mt *Trie) Delete(d []byte) (bool, error) {
if err != nil {
return false, err
}
- found, err := pnode.find(mt.cache, d[:])
+ found, err := pnode.find(&mt.cache, d[:])
if !found || err != nil {
return false, err
}
@@ -198,7 +196,7 @@ func (mt *Trie) Delete(d []byte) (bool, error) {
return true, nil
}
var updatedRoot storedNodeIdentifier
- updatedRoot, err = pnode.remove(mt.cache, d[:], make([]byte, 0, len(d)))
+ updatedRoot, err = pnode.remove(&mt.cache, d[:], make([]byte, 0, len(d)))
if err != nil {
mt.cache.rollbackTransaction()
return false, err
@@ -218,7 +216,7 @@ func (mt *Trie) GetStats() (stats Stats, err error) {
if err != nil {
return Stats{}, err
}
- err = pnode.stats(mt.cache, &stats, 1)
+ err = pnode.stats(&mt.cache, &stats, 1)
return
}
@@ -252,7 +250,7 @@ func (mt *Trie) Evict(commit bool) (int, error) {
// serialize serializes the trie root
func (mt *Trie) serialize() []byte {
serializedBuffer := make([]byte, 5*binary.MaxVarintLen64) // allocate the worst-case scenario for the trie header.
- version := binary.PutUvarint(serializedBuffer[:], MerkleTreeVersion)
+ version := binary.PutUvarint(serializedBuffer[:], merkleTreeVersion)
root := binary.PutUvarint(serializedBuffer[version:], uint64(mt.root))
next := binary.PutUvarint(serializedBuffer[version+root:], uint64(mt.nextNodeID))
elementLength := binary.PutUvarint(serializedBuffer[version+root+next:], uint64(mt.elementLength))
@@ -260,30 +258,30 @@ func (mt *Trie) serialize() []byte {
return serializedBuffer[:version+root+next+elementLength+pageSizeLength]
}
-// serialize serializes the trie root
+// deserialize deserializes the trie root
func (mt *Trie) deserialize(bytes []byte) (int64, error) {
version, versionLen := binary.Uvarint(bytes[:])
if versionLen <= 0 {
- return 0, ErrRootPageDecodingFailuire
+ return 0, ErrRootPageDecodingFailure
}
- if version != MerkleTreeVersion {
- return 0, ErrRootPageDecodingFailuire
+ if version != merkleTreeVersion {
+ return 0, ErrRootPageDecodingFailure
}
root, rootLen := binary.Uvarint(bytes[versionLen:])
if rootLen <= 0 {
- return 0, ErrRootPageDecodingFailuire
+ return 0, ErrRootPageDecodingFailure
}
nextNodeID, nextNodeIDLen := binary.Uvarint(bytes[versionLen+rootLen:])
if nextNodeIDLen <= 0 {
- return 0, ErrRootPageDecodingFailuire
+ return 0, ErrRootPageDecodingFailure
}
elemLength, elemLengthLength := binary.Uvarint(bytes[versionLen+rootLen+nextNodeIDLen:])
if elemLengthLength <= 0 {
- return 0, ErrRootPageDecodingFailuire
+ return 0, ErrRootPageDecodingFailure
}
pageSize, pageSizeLength := binary.Uvarint(bytes[versionLen+rootLen+nextNodeIDLen+elemLengthLength:])
if pageSizeLength <= 0 {
- return 0, ErrRootPageDecodingFailuire
+ return 0, ErrRootPageDecodingFailure
}
mt.root = storedNodeIdentifier(root)
mt.nextNodeID = storedNodeIdentifier(nextNodeID)
diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json
index ce40f9ab6..738eeddf8 100644
--- a/daemon/algod/api/algod.oas2.json
+++ b/daemon/algod/api/algod.oas2.json
@@ -1254,9 +1254,6 @@
"$ref": "#/definitions/ErrorResponse"
}
},
- "404": {
- "description": "Transaction simulator not enabled"
- },
"500": {
"description": "Internal Error",
"schema": {
@@ -3380,6 +3377,14 @@
"items": {
"type": "integer"
}
+ },
+ "app-budget-added": {
+ "description": "Total budget added during execution of app calls in the transaction group.",
+ "type": "integer"
+ },
+ "app-budget-consumed": {
+ "description": "Total budget consumed during execution of app calls in the transaction group.",
+ "type": "integer"
}
}
},
@@ -3396,6 +3401,14 @@
"missing-signature": {
"description": "A boolean indicating whether this transaction is missing signatures",
"type": "boolean"
+ },
+ "app-budget-consumed": {
+ "description": "Budget used during execution of an app call transaction. This value includes budged used by inner app calls spawned by this transaction.",
+ "type": "integer"
+ },
+ "logic-sig-budget-consumed": {
+ "description": "Budget used during execution of a logic sig transaction.",
+ "type": "integer"
}
}
},
diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml
index 11435901d..3b7df350f 100644
--- a/daemon/algod/api/algod.oas3.yml
+++ b/daemon/algod/api/algod.oas3.yml
@@ -1838,6 +1838,14 @@
"SimulateTransactionGroupResult": {
"description": "Simulation result for an atomic transaction group",
"properties": {
+ "app-budget-added": {
+ "description": "Total budget added during execution of app calls in the transaction group.",
+ "type": "integer"
+ },
+ "app-budget-consumed": {
+ "description": "Total budget consumed during execution of app calls in the transaction group.",
+ "type": "integer"
+ },
"failed-at": {
"description": "If present, indicates which transaction in this group caused the failure. This array represents the path to the failing transaction. Indexes are zero based, the first element indicates the top-level transaction, and successive elements indicate deeper inner transactions.",
"items": {
@@ -1865,6 +1873,14 @@
"SimulateTransactionResult": {
"description": "Simulation result for an individual transaction",
"properties": {
+ "app-budget-consumed": {
+ "description": "Budget used during execution of an app call transaction. This value includes budged used by inner app calls spawned by this transaction.",
+ "type": "integer"
+ },
+ "logic-sig-budget-consumed": {
+ "description": "Budget used during execution of a logic sig transaction.",
+ "type": "integer"
+ },
"missing-signature": {
"description": "A boolean indicating whether this transaction is missing signatures",
"type": "boolean"
@@ -5803,10 +5819,6 @@
},
"description": "Invalid API Token"
},
- "404": {
- "content": {},
- "description": "Transaction simulator not enabled"
- },
"500": {
"content": {
"application/json": {
diff --git a/daemon/algod/api/client/restClient.go b/daemon/algod/api/client/restClient.go
index f3f541573..c9bdb0a83 100644
--- a/daemon/algod/api/client/restClient.go
+++ b/daemon/algod/api/client/restClient.go
@@ -45,10 +45,11 @@ const (
// rawRequestPaths is a set of paths where the body should not be urlencoded
var rawRequestPaths = map[string]bool{
- "/v2/transactions": true,
- "/v2/teal/dryrun": true,
- "/v2/teal/compile": true,
- "/v2/participation": true,
+ "/v2/transactions": true,
+ "/v2/teal/dryrun": true,
+ "/v2/teal/compile": true,
+ "/v2/participation": true,
+ "/v2/transactions/simulate": true,
}
// unauthorizedRequestError is generated when we receive 401 error from the server. This error includes the inner error
@@ -284,7 +285,7 @@ func (client RestClient) WaitForBlock(round basics.Round) (response model.NodeSt
return
}
-// HealthCheck does a health check on the the potentially running node,
+// HealthCheck does a health check on the potentially running node,
// returning an error if the API is down
func (client RestClient) HealthCheck() error {
return client.get(nil, "/health", nil)
@@ -629,6 +630,12 @@ func (client RestClient) RawDryrun(data []byte) (response []byte, err error) {
return
}
+// SimulateRawTransaction gets the raw transaction or raw transaction group, and returns relevant simulation results.
+func (client RestClient) SimulateRawTransaction(data []byte) (response model.SimulateResponse, err error) {
+ err = client.submitForm(&response, "/v2/transactions/simulate", data, "POST", false /* encodeJSON */, true /* decodeJSON */, false)
+ return
+}
+
// StateProofs gets a state proof that covers a given round
func (client RestClient) StateProofs(round uint64) (response model.StateProofResponse, err error) {
err = client.get(&response, fmt.Sprintf("/v2/stateproofs/%d", round), nil)
@@ -670,7 +677,6 @@ func (client RestClient) GetParticipationKeyByID(participationID string) (respon
func (client RestClient) RemoveParticipationKeyByID(participationID string) (err error) {
err = client.delete(nil, fmt.Sprintf("/v2/participation/%s", participationID), nil, true)
return
-
}
/* Endpoint registered for follower nodes */
diff --git a/daemon/algod/api/server/v2/generated/data/routes.go b/daemon/algod/api/server/v2/generated/data/routes.go
index 455667080..192dc5095 100644
--- a/daemon/algod/api/server/v2/generated/data/routes.go
+++ b/daemon/algod/api/server/v2/generated/data/routes.go
@@ -145,174 +145,176 @@ 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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",
- "2dXIB5plEpTqY/kjz3eE8TSvMiBaUq5oaj4pcsn0mug1U8R1JowTwYGIJdHrVmOyZJBn6sBP8rcK5C6Y",
- "pRt8eEpXDYqJFDn08XwhigXj4LGCGql6QYgWJIMlNlpTTcwIBlffUAuigMp0TZZC7kHVIhHiC7wqZsfv",
- "Zgp4BhJXKwW2wf8uJcDvkGgqV6BnH+axyS01yESzIjK1U0d9CarKtSLYFue4YhvgxPQ6ID9USpMFEMrJ",
- "229fkCdPnjw3Eymo1pA5JhucVTN6OCfbfXY8y6gG/7nPazRfCUl5ltTt3377Asc/cxOc2ooqBfHNcmK+",
- "kNOXQxPwHSMsxLiGFa5Di/tNj8imaH5ewFJImLgmtvGdLko4/mddlZTqdF0KxnVkXQh+JfZzVIYF3cdk",
- "WI1Aq31pKCUN0HdHyfMPHx/NHx1d/du7k+S/3Z/PnlxNnP6LGu4eCkQbppWUwNNdspJAcbesKe/T463j",
- "B7UWVZ6RNd3g4tMCRb3rS0xfKzo3NK8Mn7BUipN8JRShjo0yWNIq18QPTCqeGzFloDluJ0yRUooNyyCb",
- "G+l7uWbpmqRUWRDYjlyyPDc8WCnIhngtPruRzXQVksTgdSN64IT+vMRo5rWHErBFaZCkuVCQaLHnePIn",
- "DuUZCQ+U5qxS1zusyPkaCA5uPtjDFmnHDU/n+Y5oXNeMUEUo8UfTnLAl2YmKXOLi5OwC+7vZGKoVxBAN",
- "F6d1jprNO0S+HjEixFsIkQPlSDy/7/ok40u2qiQocrkGvXZnngRVCq6AiMU/IdVm2f/z7MfXREjyAyhF",
+ "2dXIB5plEpTqY/kjz3eE8TSvMiBaUq5oaj4psmF6TfSaKeI6E8aJ4EDEkuh1qzFZMsgzdeAn+VsFchfM",
+ "0g0+PKWrBsVEihz6eL4QxYJx8FhBjVS9IEQLksESG62pJmYEg6tvqAVRQGW6Jksh96BqkQjxBV4Vs+N3",
+ "MwU8A4mrlQK7xP8uJcDvkGgqV6BnH+axyS01yESzIjK1U0d9CarKtSLYFue4YpfAiel1QH6olCYLIJST",
+ "t9++IE+ePHluJlJQrSFzTDY4q2b0cE62++x4llEN/nOf12i+EpLyLKnbv/32BY5/5iY4tRVVCuKb5cR8",
+ "IacvhybgO0ZYiHENK1yHFvebHpFN0fy8gKWQMHFNbOM7XZRw/M+6KinV6boUjOvIuhD8SuznqAwLuo/J",
+ "sBqBVvvSUEoaoO+OkucfPj6aPzq6+rd3J8l/uz+fPbmaOP0XNdw9FIg2TCspgae7ZCWB4m5ZU96nx1vH",
+ "D2otqjwja3qJi08LFPWuLzF9rei8pHll+ISlUpzkK6EIdWyUwZJWuSZ+YFLx3IgpA81xO2GKlFJcsgyy",
+ "uZG+mzVL1ySlyoLAdmTD8tzwYKUgG+K1+OxGNtNVSBKD143ogRP68xKjmdceSsAWpUGS5kJBosWe48mf",
+ "OJRnJDxQmrNKXe+wIudrIDi4+WAPW6QdNzyd5zuicV0zQhWhxB9Nc8KWZCcqssHFydkF9nezMVQriCEa",
+ "Lk7rHDWbd4h8PWJEiLcQIgfKkXh+3/VJxpdsVUlQZLMGvXZnngRVCq6AiMU/IdVm2f/z7MfXREjyAyhF",
"V/CGphcEeCoyyA7I6ZJwoQPWcLyENDQ9h+bh8Iod8v9UwvBEoVYlTS/iJ3rOChaZ1Q90y4qqILwqFiDN",
"kvojRAsiQVeSDyFkIe5hxYJu+4Oey4qnuP7NsC1dznAbU2VOd0iwgm6/Opo7dBSheU5K4BnjK6K3fFCP",
"M2PvRy+RouLZBDVHmzUNDlZVQsqWDDJSQxnBxA2zDx/Gr4dPo3wF6Hggg+jUo+xBh8M2wjNmd5svpKQr",
- "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",
+ "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",
- "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",
+ "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",
+ "xwAA",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/daemon/algod/api/server/v2/generated/experimental/routes.go b/daemon/algod/api/server/v2/generated/experimental/routes.go
index 3e8edb58b..cce4c207c 100644
--- a/daemon/algod/api/server/v2/generated/experimental/routes.go
+++ b/daemon/algod/api/server/v2/generated/experimental/routes.go
@@ -211,30 +211,32 @@ var swaggerSpec = []string{
"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",
- "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",
+ "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",
+ "AA==",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/daemon/algod/api/server/v2/generated/model/types.go b/daemon/algod/api/server/v2/generated/model/types.go
index 583705783..e838323e7 100644
--- a/daemon/algod/api/server/v2/generated/model/types.go
+++ b/daemon/algod/api/server/v2/generated/model/types.go
@@ -610,6 +610,12 @@ type PendingTransactionResponse struct {
// SimulateTransactionGroupResult Simulation result for an atomic transaction group
type SimulateTransactionGroupResult struct {
+ // AppBudgetAdded Total budget added during execution of app calls in the transaction group.
+ AppBudgetAdded *uint64 `json:"app-budget-added,omitempty"`
+
+ // AppBudgetConsumed Total budget consumed during execution of app calls in the transaction group.
+ AppBudgetConsumed *uint64 `json:"app-budget-consumed,omitempty"`
+
// FailedAt If present, indicates which transaction in this group caused the failure. This array represents the path to the failing transaction. Indexes are zero based, the first element indicates the top-level transaction, and successive elements indicate deeper inner transactions.
FailedAt *[]uint64 `json:"failed-at,omitempty"`
@@ -622,6 +628,12 @@ type SimulateTransactionGroupResult struct {
// SimulateTransactionResult Simulation result for an individual transaction
type SimulateTransactionResult struct {
+ // AppBudgetConsumed Budget used during execution of an app call transaction. This value includes budged used by inner app calls spawned by this transaction.
+ AppBudgetConsumed *uint64 `json:"app-budget-consumed,omitempty"`
+
+ // LogicSigBudgetConsumed Budget used during execution of a logic sig transaction.
+ LogicSigBudgetConsumed *uint64 `json:"logic-sig-budget-consumed,omitempty"`
+
// MissingSignature A boolean indicating whether this transaction is missing signatures
MissingSignature *bool `json:"missing-signature,omitempty"`
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 5bf532b49..3645c5040 100644
--- a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go
+++ b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go
@@ -266,35 +266,37 @@ var swaggerSpec = []string{
"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",
- "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",
+ "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",
+ "4/X/BAAA///WXlTym8AAAA==",
}
// GetSwagger returns the content of the embedded swagger specification file
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 7c89fbd8b..36c2df859 100644
--- a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go
+++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go
@@ -572,235 +572,236 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "H4sIAAAAAAAC/+x9a3Mbt5LoX0Fxt8qP5VB+Zk9Uldor23loYzsuS8nuObFvAs40SRwNgTkARiLj6/9+",
- "Cw1gBjODIYcSLdmJPtni4NFoNBqNfn4YpWJZCA5cq9Hhh1FBJV2CBol/0TQVJdcJy8xfGahUskIzwUeH",
- "/htRWjI+H41HzPxaUL0YjUecLqFuY/qPRxL+VTIJ2ehQyxLGI5UuYEnNwHpdmNbVSKtkLhI3xJEd4vjF",
- "6OOGDzTLJCjVhfInnq8J42leZkC0pFzR1HxS5ILpBdELpojrTBgnggMRM6IXjcZkxiDP1MQv8l8lyHWw",
- "Sjd5/5I+1iAmUuTQhfO5WE4ZBw8VVEBVG0K0IBnMsNGCamJmMLD6hloQBVSmCzITcguoFogQXuDlcnT4",
- "60gBz0DibqXAzvG/MwnwBySayjno0ftxbHEzDTLRbBlZ2rHDvgRV5loRbItrnLNz4MT0mpBXpdJkCoRy",
- "8va75+Tx48dfm4UsqdaQOSLrXVU9e7gm2310OMqoBv+5S2s0nwtJeZZU7d9+9xznP3ELHNqKKgXxw3Jk",
- "vpDjF30L8B0jJMS4hjnuQ4P6TY/Ioah/nsJMSBi4J7bxXjclnP9GdyWlOl0UgnEd2ReCX4n9HOVhQfdN",
- "PKwCoNG+MJiSZtBfHyRfv//wcPzwwcd/+/Uo+Yf78+njjwOX/7wadwsGog3TUkrg6TqZS6B4WhaUd/Hx",
- "1tGDWogyz8iCnuPm0yWyeteXmL6WdZ7TvDR0wlIpjvK5UIQ6MspgRstcEz8xKXlu2JQZzVE7YYoUUpyz",
- "DLKx4b4XC5YuSEqVHQLbkQuW54YGSwVZH63FV7fhMH0MUWLguhQ+cEGfLzLqdW3BBKyQGyRpLhQkWmy5",
- "nvyNQ3lGwgulvqvUbpcVOV0AwcnNB3vZIu64oek8XxON+5oRqggl/moaEzYja1GSC9ycnJ1hf7cag7Ul",
- "MUjDzWnco+bw9qGvg4wI8qZC5EA5Is+fuy7K+IzNSwmKXCxAL9ydJ0EVgisgYvpPSLXZ9v8++ek1EZK8",
- "AqXoHN7Q9IwAT0UG2YQczwgXOiANR0uIQ9Ozbx0Ortgl/08lDE0s1byg6Vn8Rs/ZkkVW9Yqu2LJcEl4u",
- "pyDNlvorRAsiQZeS9wFkR9xCiku66k56Kkue4v7X0zZkOUNtTBU5XSPClnT1zYOxA0cRmuekAJ4xPid6",
- "xXvlODP3dvASKUqeDRBztNnT4GJVBaRsxiAj1SgbIHHTbIOH8d3gqYWvABw/SC841SxbwOGwitCMOd3m",
- "CynoHAKSmZCfHXPDr1qcAa8InUzX+KmQcM5EqapOPTDi1JslcC40JIWEGYvQ2IlDh2Ewto3jwEsnA6WC",
- "a8o4ZIY5I9BCg2VWvTAFE25+73Rv8SlV8NWTvju+/jpw92eivesbd3zQbmOjxB7JyNVpvroDG5esGv0H",
- "vA/DuRWbJ/bnzkay+am5bWYsx5von2b/PBpKhUyggQh/Nyk251SXEg7f8fvmL5KQE015RmVmflnan16V",
- "uWYnbG5+yu1PL8WcpSds3oPMCtbogwu7Le0/Zrw4O9ar6LvipRBnZREuKG08XKdrcvyib5PtmLsS5lH1",
- "2g0fHqcr/xjZtYdeVRvZA2Qv7gpqGp7BWoKBlqYz/Gc1Q3qiM/mH+acoctNbF7MYag0duysZ1QdOrXBU",
- "FDlLqUHiW/fZfDVMAOxDgtYtDvBCPfwQgFhIUYDUzA5KiyLJRUrzRGmqcaR/lzAbHY7+7aDWvxzY7uog",
- "mPyl6XWCnYzIasWghBbFDmO8MaKP2sAsDIPGT8gmLNtDoYlxu4mGlJhhwTmcU64n9ZOlwQ+qA/yrm6nG",
- "t5V2LL5bT7BehBPbcArKSsC24R1FAtQTRCtBtKJAOs/FtPrh7lFR1BjE70dFYfGB0iMwFMxgxZRW93D5",
- "tD5J4TzHLybk+3BsFMUFz9fmcrCihrkbZu7WcrdYpVtya6hHvKMIbqeQE7M1Hg1GzN8HxeGzYiFyI/Vs",
- "pRXT+AfXNiQz8/ugzl8GiYW47ScufGg5zNk3Dv4SPG7utiinSzhO3TMhR+2+lyMbM0qcYC5FKxv30467",
- "AY8VCi8kLSyA7ou9SxnHR5ptZGG9IjcdyOiiMAdnOKA1hOrSZ23reYhCgqTQguFZLtKzH6ha7OHMT/1Y",
- "3eOH05AF0AwkWVC1mIxiUkZ4vOrRhhwx0xAf+GQaTDWplriv5W1ZWkY1DZbm4I2LJRb12A+ZHsjI2+Un",
- "/A/NiflszrZh/XbYCTlFBqbscXZGhsy89u0Dwc5kGqAWQpClfeAT8+reCcrn9eTxfRq0R99anYLbIbcI",
- "3CGx2vsxeCZWMRieiVXnCIgVqH3QhxkHxUgNSzUAvhcOMoH779BHpaTrLpJx7CFINgs0oqvC08DDG9/M",
- "Uitnj6ZCXo77tNgKJ7XKmVAzasB8xy0kYdOySBwpRtRWtkFroNrKt5lptIePYayBhRNNPwEWlBl1H1ho",
- "DrRvLIhlwXLYA+kvokx/ShU8fkROfjh6+vDRb4+efmVIspBiLumSTNcaFLnr3mZE6XUO97orw9dRmev4",
- "6F898YrK5rixcZQoZQpLWnSHsgpQKwLZZsS062KtiWZcdQXgkMN5CoaTW7QTq9s3oL1gykhYy+leNqMP",
- "YVk9S0YcJBlsJaZdl1dPsw6XKNey3MdTFqQUMqJfwyOmRSry5BykYiJiTXnjWhDXwou3Rft3Cy25oIqY",
- "uVH1W3IUKCKUpVd8ON+3Q5+ueI2bjZzfrjeyOjfvkH1pIt9rEhUpQCZ6xUkG03LeeAnNpFgSSjLsiHf0",
- "96BP1jxFrdo+iLT/mbZkHFX8as3T4M1mNiqHbN7YhKu/zdpY8fo5O9UdFQHHoOMlfsZn/QvINd27/NKe",
- "IAb7c7+RFliSmYb4Cn7J5gsdCJhvpBCz/cMYmyUGKH6w4nlu+nSF9NciA7PYUu3hMq4Hq2nd7GlI4XQq",
- "Sk0o4SID1KiUKn5N91ju0WSIlk4d3vx6YSXuKRhCSmlpVlsWBO14Hc5Rd0xoaqk3QdSoHitGZX6yrex0",
- "1iqcS6CZedUDJ2LqTAXOiIGLpGiE1P6ic0JC5Cw14CqkSEEpyBKnotgKmm9nmYjegCcEHAGuZiFKkBmV",
- "Vwb27HwrnGewTtBkrsjdH39R924AXi00zbcgFtvE0Fs9+Jw9qAv1sOk3EVx78pDsqATiea55XRoGkYOG",
- "PhTuhJPe/WtD1NnFq6PlHCRaZj4pxftJrkZAFaifmN6vCm1Z9DiCuYfOKVui3o5TLhSkgmcqOlhOlU62",
- "sWXTqPEaMysIOGGME+PAPULJS6q0tSYynqESxF4nOI8VUMwU/QD3CqRm5F+8LNodOzX3IFelqgRTVRaF",
- "kBqy2Bo4rDbM9RpW1VxiFoxdSb9akFLBtpH7sBSM75BlV2IRRHWldHfm9u7iUDVt7vl1FJUNIGpEbALk",
- "xLcKsBs6w/QAwlSNaEs4TLUop/LAGY+UFkVhuIVOSl7160PTiW19pH+u23aJi+r63s4EKPTBce0d5BcW",
- "s9YNakHNExpHJkt6ZmQPfBBbs2cXZnMYE8V4CskmyjfH8sS0Co/A1kNaFnNJM0gyyOm6O+jP9jOxnzcN",
- "gDteP3yEhsT6s8Q3vaZk7z6wYWiB46mY8EjwC0nNETQvj5pAXO8tI2eAY8eYk6OjO9VQOFd0i/x4uGy7",
- "1ZER8TY8F9rsuCUHhNgx9CHw9qChGvnymMDOSf0sa0/xd1BugkqM2H2SNai+JdTj77SAHmWa8xQOjkuL",
- "u7cYcJRr9nKxLWyk78T2aPbeUKlZygp86vwI672//NoTRO1NJANNWQ4ZCT7YV2AR9ifWEaM95uVegoOU",
- "MF3wO1qYyHJyplDiaQJ/Bmt8cr+xHn6ngV/gHp6ykVHN9UQ5QUC935CRwMMmsKKpztdGTtMLWJMLkEBU",
- "OV0yra3nbvOlq0WRhANEFdwbZnTWHOsd53dgiHnpBIcKltfdivHIPgk2w3faehc00OGeAoUQ+QDlUQcZ",
- "UQgGGf5JIcyuM+dE7N1IPSU1gHRMG0151e1/RzXQjCsgfxclSSnHF1epoRJphEQ5AeVHM4ORwKo5nYm/",
- "xhDksAT7kMQv9++3F37/vttzpsgMLrznvWnYRsf9+6jGeSOUbhyuPagKzXE7jlwfqPnHe885L7R4ynYT",
- "sxt5yE6+aQ1emQvMmVLKEa5Z/pUZQOtkroasPaSRYeZ1HHeQUj8YOrZu3PcTtixzqvdhvtgoj1bvCbZc",
- "QsaohnxNCgkpWO9qI2ApC4sBjVi/q3RB+RzlainKuXP8seMgYyyV1WDIkneGiAofesWTuRRlEWOUztnT",
- "O9gbsQOoefkEiMTOVs6/oNV8LqZiyA3mER7szvdmzD6rwnjU+zA0SD2vH4YWOc0ogTgWMOwhUWWaAkRd",
- "gGNPrmqprWjIOr7FDWjEhlJaHyhCU13SPKQ6cjwjlK+bYZKU5cpwQaYItjOda7/asV2bj2GZ0dzaZiNB",
- "FeFJaUh8wc7XKG2jYqDdAYnESENdyggJ0BwvQ8afRodfDx2Dsjtx4HRVf+zzuzLv73y9BzHIDkQkFBIU",
- "Xlqh3krZr2IWxj65W02tlYZlV7Vvu/7Ww2je9j4gBc8Zh2QpOKyj4b6Mwyv8GGUceHH2dEYRpq9v+1XS",
- "gL8FVnOeIdR4Vfzibge86E3lcLiHzW+P27LqhFFfqLWEvCCUpDlDnabgSssy1e84Ra1JcNgijhn+fdiv",
- "R3vum8QVdxG9mhvqHafolFPpUqLG5BlEFAffAXh1mirnc1At/klmAO+4a8U4KTnTONfS7FdiN6wAid4R",
- "E9tySdeGBaLa7w+QgkxL3eTJGHmitGGX1sRkpiFi9o5TTXIwb+pXjJ+ucDhvovU0w0FfCHlWYSF+hcyB",
- "g2IqiTuQfG+/om+fW/7C+flhpLD9bI0SZvw6PGWNSpU6+vX/3v2vw1+Pkn/Q5I8Hydf/cfD+w5OP9+53",
- "fnz08Ztv/l/zp8cfv7n3X/8e2ykPeywuwkF+/MI91o5foEReWyU6sF+bRnrJeBIlstD23qItchdjAB0B",
- "3Wvqa/QC3nG94oaQzmnOMiNyXYYc2iyucxbt6WhRTWMjWvoZv9Yd5dwrcBkSYTIt1njpa7zrcxWPQEIz",
- "mQsqwvMyK7ndSi/oWgd77/siZuMqyswmoDgkGIK0oN5xy/356OlXo3EdOlR9H41H7uv7CCWzbBWVDmEV",
- "e764A4IH444iBV0r6BFAEfaom4/1NgiHXYJ596oFK66fUyjNpnEO592WnRpkxY+59Sc25weNbmunyxez",
- "64dbSyOHF3oRC0xvSArYqt5NgJYjRCHFOfAxYROYtNUQmXmaOYejHOgMA6TxoSeGhGFU58ASmqeKAOvh",
- "Qga99WP0g8Kt49YfxyN3+au9y+Nu4Bhc7TkrC5v/Wwty5/tvT8mBY5jqjo1VtEMH0WWRV6sLoGi4yBhu",
- "ZtNx2GDNd/wdfwEzxpn5fviOZ1TTgylVLFUHpQL5jOaUpzCZC3LoYzJeUE3f8Y6k1ZsxJ4iGIUU5zVlK",
- "zkKJuCZPmwWhO8K7d7/SfC7evXvf8Rboyq9uqih/sRMkF0wvRKkTF8OdSLigMmaNUVUML45skzRsmnVM",
- "3NiWFbsYcTd+nOfRolDtWL7u8osiN8sPyFC5SDWzZURpIb0sYgQUCw3u72vhLgZJL7wKo1SgyO9LWvzK",
- "uH5PknflgwePgTSC2353V76hyXUBgxUZvbGGbf0FLty+a2ClJU0KOo9Zfd69+1UDLXD3UV5e4iM7zwl2",
- "awTVeadhHKpegMdH/wZYOHYOEMLFndhePl9PfAn4CbcQ2xhxozZFX3a/gjC7S29XK1Svs0ulXiTmbEdX",
- "pQyJ+52p0njMjZDl/QMUm6MPpst4MgWSLiA9c6koYFno9bjR3bugOEHTsw6mbJISGySDYfKoM58CKYuM",
- "OlG8rUGarokCrb0T6Fs4g/WpqKPsdwlQbsbLqr6DipQaSJeGWMNj68Zob77zc0IVV1H4sFOMP/JkcVjR",
- "he/Tf5CtyLuHQxwjikY8Zx8iqIwgwhJ/DwousVAz3pVIP7Y888qY2psvkrDE837imtSPJ+eSFK4GFdz2",
- "+xIw45G4UGRKjdwuXLIeGxMacLFS0Tn0SMih2WJg5GXD1IGDbLv3ojedmLUvtM59EwXZNk7MmqOUAuaL",
- "IRV8zLQc0fxM1jLmjACYg88hbJqjmFR57FmmQ2XDfGSTivWBFidgkLwWODwYTYyEks2CKp9HCNMt+bM8",
- "SAb4hDHOmzJbhAr9IKdSpV/3PLd9TjuvS5ffwie18JkswqflgKwURsJHt+3YdgiOAlAGOcztwm1jTyh1",
- "vHW9QQaOn2aznHEgScwdiyolUmYTQdXXjJsDjHx8nxCrAiaDR4iRcQA2WnxxYPJahGeTz3cBkrt4cerH",
- "Rltx8DfEQ1usg7IReURhWDjrMSClngNQ58NX3V8tT1IchjA+JobNndPcsDn34qsH6SRYQLG1lU7B+Rzc",
- "6xNnN2jg7cWy05rsVXSZ1YQykwc6LtBtgHgqVomNbYtKvNPV1NB71GcbI+1iB9OmsrijyFSs0I8Frxbr",
- "I7wFln44PBjBC3/FFNIr9uu7zS0wm6bdLE3FqFAhyTh1XkUufeLEkKl7JJg+crkbZKe4FAAtZUed6tU9",
- "frc+UpviSfcyr2+1cZ11yYfDxI5/3xGK7lIP/rpamCqfxJu2xBLVUzTdMZqpNAIRMkb0hk10jTRdU5CC",
- "HPBRkDSEqOQsZrozbxvAG+fEdwuUF5iwg/L1vcDHR8KcKQ21Et27JNyEepJinjAhZv2r04WcmfW9FaK6",
- "pmwiGuzYWOa1rwB9ZGdMKp2gBSK6BNPoO4WP6u9M07is1PQislk1WRbnDTjtGayTjOVlnF7dvD++MNO+",
- "rliiKqfIbxm3viFTzAIb9S3cMLV1P9244Jd2wS/p3tY77DSYpmZiacilOccXci5anHcTO4gQYIw4urvW",
- "i9INDDIICe1yx0BusocTQ0Inm7SvncOU+bG3uo34wNS+O8qOFF1LoDDYuAqGZiIjljAdJFHtxmr2nAFa",
- "FCxbtXShdtTeFzPdSeHhU0+1sIC76wbbgoFA7xkLF5GgmlnGagHfpsNtJPmYDMLMaTMXWMgQwqmY8snc",
- "u4iqwsm24eoUaP4jrH8xbXE5o4/j0dVUpzFcuxG34PpNtb1RPKNp3qrSGpaQHVFOi0KKc5onTsHcR5pS",
- "nDvSxOZeH33NrC6uxjz99ujlGwf+x/EozYHKpBIVeleF7YovZlU2oVnPAfHJos2bz8vsVpQMNr/KwhQq",
- "pS8W4LLuBtJoJz1gbXAIjqJTUs/iHkJbVc7ONmKXuMFGAkVlIqnVd9ZC0rSK0HPKcq8389D2ePPg4obl",
- "mIxyhXCAK1tXAiNZsld20znd8dNRU9cWnhTOtSEv8NKmvlZE8LYJHd2L14Wzui8pJvezWpEuc+LlEjUJ",
- "icpZGtex8qkyxMGt7cw0Jti4Rxg1I5asxxTLSxaMZZqpAQ/dFpDBHFFk+kSRfbibClfWpOTsXyUQlgHX",
- "5pPEU9k6qJhN0Wnbu9epkR26c7mBrYa+Hv4qMkaY2LJ94yEQmwWM0FLXAfdF9WT2C600UuhuXZskdjD4",
- "hzN2rsQNxnpHH46arfPiomlxC6uQdPmfIQybjnp7CRT/eHUZNnvmiJY0YSqZSfEHxN95+DyOhOL4VJ4M",
- "vVz+AD7A57zW7tSVWerZe7e7T7oJtVBNJ4UeqsedD8xymFPQa6gpt1ttKww0fN3iBBN6lR7Y8WuCcTB3",
- "PHFzejGlsYSLRsgwMB3VBuCGLl0L4jt73KsqsMHOTgJbctWW2SjrAmQdJdfN2HJJgcFOO1hUqCUDpNpQ",
- "Jhhb+1+uRGSYkl9QbgtVmH72KLneCqzyy/S6EBJzJKi42j+DlC1pHpccsrSr4s3YnNkaDKWCIMm/G8jW",
- "t7FU5AolVOE6DjXHM/JgHFQacbuRsXOm2DQHbPHQtphShZy8UkRVXczygOuFwuaPBjRflDyTkOmFsohV",
- "glRCHT5vKuPVFPQFACcPsN3Dr8ldNNspdg73DBbd/Tw6fPg1Kl3tHw9iF4CrobGJm2TITv7HsZM4HaPd",
- "0o5hGLcbdRINJ7dFtPoZ14bTZLsOOUvY0vG67WdpSTmdQ9xTZLkFJtsXdxMVaS288MxWgFFaijVhOj4/",
- "aGr4U4/3uWF/FgySiuWS6aUz7iixNPRUZ/C3k/rhbDkZl3zVw+U/oo208Cai1iPyepWm9n6LrRot2a/p",
- "EppoHRNqE2PkrPZe8CmhybHPu4PZaKsktBY3Zi6zdBRz0JlhRgrJuMaHRalnyd9IuqCSpob9TfrATaZf",
- "PYlk4G1mguS7AX7teJegQJ7HUS97yN7LEK4vucsFT5aGo2T36miP4FT2GnPjZrs+2+HmoYcKZWaUpJfc",
- "yga50YBTX4nw+IYBr0iK1Xp2osedV3btlFnKOHnQ0uzQz29fOiljKWQsmV593J3EIUFLBufouxffJDPm",
- "FfdC5oN24SrQ36zlwYucgVjmz3LsIfBMRF6nPit0pUl3vuoR7UDfMTUfDBlM3VBj0szAe/1GP6987hqf",
- "zBcPK/7RBvaGtxSR7FfQs4lBdvDodmbV98D+TckzsRq6qa0T4jf2M0BNFCUly7Nf6qjMVvJ1SXm6iNqz",
- "pqbjb3WZqGpx9n6K5qxbUM4hjw5nZcHfvMwYkWr/KYbOs2R8YNt2Pni73NbiasCbYHqg/IQGvUznZoIQ",
- "q82At8qhOp+LjOA8dYK0mnt26wgE2Z7/VYLSseAh/GCdulBvad67NtkwAZ7ha3FCvreVYBdAGulv8JVW",
- "ZRFwqW+tQr0sckGzMSZyOP326CWxs9o+ttiJTXY8x0dKcxUtfVWQ+3GYe7CvWxIPXRg+zmZfarNqpTEb",
- "ldJ0WcSCQ02LU98AI1BDHT4+X0LsTMiLoKajjSM1Qxh6mDG5NC+uajQruyBNmP9oTdMFPskaLLWf5Idn",
- "6fZUqYLKeFWFmyohIp47A7dL1G3zdI+JMO/mC6ZsAVA4h2Y8ahWc7VQCPj61uTxZcm4pJSp7bEoecBm0",
- "e+Cso4ZX80chayF+R4HcJrnfNWn5CfaKJmhqZ0DvlMSz0Y1V5RJf2DmlXHCWYnqk2NXsKoUOsYENyCTV",
- "VrL6I+5OaORwRfOuV25yDou9mdg9I3SI6yrhg69mUy112D81lqRcUE3moJXjbJCNffkApwdkXIFLcIl1",
- "ZQM+KWTDrogcMmqqTiqTxo5khGExPQ+778y31+7Zj/7iZ4yjgO/Q5lzTraYOCxlq8ypgmswFKLeeZmyw",
- "+tX0mWCYbAar9xNf+NBmg0GznFm2tUF3hzryFmlnATZtn5u2Lk9Q9XPDA9lOelQUbtL+4hJReUCveC+C",
- "I5bFxJt2AuRW44ejbSC3ja4keJ8aQoNzNERDgfdwhzCqQgutIj5GaLUUhS2IdeGKZjBgPALGS8ahLssZ",
- "uSDS6JWAG4PntaefSiXVVgQcxNNOgeZofY4xNKWd6eGqQ7VzCRmU4Br9HP3bWNeI6GEcVYNacKN8XVUD",
- "NdQdCBPPsQyxQ2S34gNKVU6IyjCioFUDIsY4DOP2VWaaF0D3GHRlIttdS2pPzi43UV+Q6LTM5qATmmWx",
- "jFTP8CvBrz65FKwgLavElEVBUsyJ0kwS06U2N1EquCqXG+byDa44XVBUJUINYWEXv8MYhDJd47+xrIz9",
- "O+OcMHZ2A/QeF64KxY5yc3OkjtRraDpRbJ4MxwTeKVdHRz315Qi97r9XSs/FvAnINaeG2MTlwj2K8bdv",
- "zcURZk7opBq1V0uV2ACd7oQvhYfPxiokt8mV8Crr5B5FY09VamuzAqK/aNYYL78e19sgIQa196u1HvY5",
- "4Ka9/uJUu8g1TclGFtQbDWS9d2zcD0IR15z2eexYhx3zudN7mGTYkbNx7I0I9a5gXYB+9H6mpKDMmcZr",
- "ZtHFrPNI71cXbjp09Qa3F+H8vHs1dj+e9/lkE8X4PAeC39tlhs7AhbNXdebtWr1Xkn8S2l9dmVc7XuUV",
- "H11/1zsBp7pZNWiv0vbUpbS3y3Rv8h9/sT5sBLiW689AhdvZ9E6Rpq60a9VTdRNSpUMelB65cSvG6y31",
- "5z+qcx4hPRVCsToFd6wQ00Bft1OspRTkb+qO5R1NziHVmHe9NqBLgF2yOZnJgiJ/t3mQet6OlUugS3+0",
- "KedRN9n6lgutE5YUhNbZRNWT4Rl+jio3KWRKmAF3DtzV2WsGHAx2e57NINXsfEsY2P8sgAchRmOvhLD1",
- "coOoMFa50WIWkd1VbDVAm6K0NsITZPO7Mjh9QSBnsL6jSIMaopmzx/5euUwCCcQAcofEkIhQMTcEqzV1",
- "lmGmKspALHi3H9sd6lRcvTV3gqDGS87lSdLcuHWg44Yp40U/Bs1luu4U/oseoX2RYt2iAf3C9gus0aCq",
- "eng+AUX4JCXH3TR9Fy6BBQbtVYYCn8oClP/NR+jaWXJ2BmFVIDTLXFCZ+RZRPYNXYSQb7qNOeJdPeN8G",
- "elbNzGonzW5ATyTxE7riprkw8lfS58/c9IsMi+ej94dN+Y0enwauGUhXPQ2FvVwoSLTwTp2b4NiEClfo",
- "/TJIUL3JFi1wvSlQ3tY5XjDpLMWUJ9R5toQLJBKW1EAng0ws/XNuQvZz+91HsPiko1vVKRW9bk80791z",
- "meogMaT6GXG35fbImMtoVhjntlariqVl4QaVoeq/kCIrU3tBhwej0j4NTnq0gZVElRJpd5Wd92WOKcBe",
- "BnGGZ7A+sKK/T9XvtzKE3opQdg1BXH9rt/eqdIq/r/O5XcB8L3DepOJmPCqEyJMeXf9xN7tM+wycsfQM",
- "MmLuDu/Y1lO2hNxFFXNlzL1YrH02laIADtm9CSFH3LoSe7tuM71xa3J+R2+af4WzZqVN+OR0SpN3PO6T",
- "iamY5BX5mx9mM1dTYJjfFaeyg2zJXbLqyWwj6UWkiM9k6KO0a2ltF1apicpCEZNStpSwiFiRfU0EX2HD",
- "R6xosWRpt4pCR5SYYTWqhEYGP64Y+LhRK5C1Cnf4HEO2TENKrQBnHg+U5aUEFzlgy+a00ukXVC/89pnm",
- "XTHLXNmg0K3fpmSnyj4K/OPE1expnwtRJDmcQ8OQ4MIZyjQFpdg5hPV+bGeSART4VG9fIDENeUhXLR7i",
- "1p4EOtYh2I0yFYtYu1NkC8foKcaeWPJQQ0nIQHTOspI28KeuUIplYG33ENaBJ2TnwxFfXOdouHIpSZXM",
- "LabIdOEkfgsN/dalXVoCUlCCpRqzpxZlhYWrCCO9qI1j9nJJNAbRQ1enHTkyQdmVzZqXMMdO7bwrrWkE",
- "X2r+1LW39FV9GocVgPEdtoAXKuSCEjBeEnLg3LCH7asKKcFSeimhsfxtOj63wJp9BVtkebdZps14Zr2z",
- "mvsSKHDV80ov2leXqa0+xYQ6gmOSsa7aVaGpDHOVh4RjeLc8p/n1q04x09IR4sPVuY0vNNS9hUi2qFSX",
- "c3N7SQfNHejZ9jc1f4Oq3v8Bs0dRG6cbytk8KlnBW4aQZdKc5KKucIdDkgsc0xpFH35Fpi5Ep5CQMsVa",
- "0YsXPo1ypWrCqgJ1+ePNuq1t6/xF6CuQ8cyLL+R1nZJVC7wxagjrI3rDTKXn5EapPEZ9HbKI4C/Go8Jc",
- "GVuui7OGtdSmuG65AQoJe7aaBv5PO1pNu1lAhi7PWgbNpVMq6K5z8G3dwG3koq7XNtTk30XuprydQyz1",
- "8XS8pju6CliEYC5rgqCS3x/+TiTMsFiNIPfv4wT3749d098fNT+b43z/frzM8nU5CVgcuTHcvDGK+aXP",
- "bdy6RvdEKLT2o2R5to0wGvEmdbknjKj4zUWc3UjBqd+sLad7VF3Rj13ck9qbgIiJrLUxeTBVEEkyIIjE",
- "dYuEjKBWJC0l02tMhONV/+y3qDvD95W10Fmbq9QJ7u7T4gyqVEq1bbFU/nb9XtAc7yMjU6NzmMbSut+u",
- "6LLIwR2Ub+5M/xMe/+1J9uDxw/+c/u3B0wcpPHn69YMH9Osn9OHXjx/Co789ffIAHs6++nr6KHv05NH0",
- "yaMnXz39On385OH0yVdf/+cdw4cMyBbQkQ+7Hv0vVmVLjt4cJ6cG2BontGBVRW1Dxr60DE3xJMKSsnx0",
- "6H/6P/6ETVKxrIf3v45cVOdooXWhDg8OLi4uJmGXgzkaExItynRx4OfpVjJ+c1xF5tinJe6oDbrwKgNP",
- "Ckf47e23J6fk6M3xJKiUeTh6MHkweYiFFAvgtGCjw9Fj/AlPzwL3/cAR2+jww8fx6GABNEfbu/ljCVqy",
- "1H9SF3Q+BzlxNXbMT+ePDrwocfDBGVI+mlHnsRRBNsYoCCzplp5xRll01LQxRI1U7splFh9XCf6d1oJn",
- "GPphbROGtVXIOs7qTLbHNaPy+XxsgsPDXyOVB2dsbt7RjYqqrVqt5uX93yc/vSZCEvekeUPTs8pthhzP",
- "bG4GKc4ZRhRkQRiK6TnxNPuvEuS6pinH7cLkfT5fu4vTWKp50XRqriWpWEHxWJkfnNmQQkDMldmzZlZa",
- "lhBCUrNew04fJF+///D0bx9HAwBBG7wCTO3wO83z322FWFihIdMnR3LJL8aR3OQoQY9rMxp2qHdyjF7Z",
- "1dew/EzVphkL9DsXHH7v2wYHWHQfaJ6bhoJDbA/eY/IBJBY8Z48ePNhb3aoq/M36dlejeJK4xEBdJmQ/",
- "RUrR+vJVPXVon+xxoU3n1Ssvtz1cZ9HPaIYlQUBpu5SHX+xSjjm6wZhLgdhL7+N49PQL3ptjbngOzQm2",
- "DDL7dC+an/kZFxfctzQCT7lcUrlGcSaoW9QKraVzhRYVZJH2bDcqlYzef+y99Q7CQgwHHxqeFNmV7sRO",
- "DZrjF1uuyTuqj3N282K26jyY71Uaf7S1u2IWWFhA3ZuQ78PeyL0xzYRN4lBKDpl3hPC3XpU3y2fjqmG7",
- "o8IMHNFLO1AR397fN31/HzUVHI3cizFgGqdgI0wdb6yrXqDdCKpWJb9LVcoLKi5cIm/1Jy0n1Hpf2pne",
- "x55/Wxn1Le56cNcnJgXwVhJTs1LGp2fNPmKhukkaV8YnZNxfuND3iuaGToLltqK5bULSW2HwLyMMVg66",
- "tiCvz8F9NfEQq/EcfPBJZvcgErokuwOEwfBZHfQNkqDebbGTexObMTZsczme4Txyt4p5mPr3VsD7DAS8",
- "blrtGBh1suSbE+oQhkWdd3uXarqNMlk75Qf/QqW4vzCyesU2A+l2ge0S7LMjjDlm/cnY6p9SCHNIuxW/",
- "/tLiVxUncyUBrJEY30VeBWasK2nv2to5pitJrBkrFXC2qgyxO8LjuoiPYTGYV8mn1FBj/zJEE6p9NNrN",
- "GnfejV0R63sIH6jP1scvtklXX5CeZ3B+v8gtEN+bT81Lo2aHt9djdhjGm548eHJ9EIS78Fpo8h3e4p+Y",
- "Q35SlhYnq11Z2CaOdDC1mYc3cSXeYkvIKOqMwgGPwsINYdZi65Fx15XLDDNB3JsQn99YVdUaXP6BuaB5",
- "nWeJyrntZHicQQK54/88xPHvTMh3GBGg1Rgdy7RL5U/uMK4PHz56/MQ1kfTC+m21202/enJ49M03rlmd",
- "zdq+bzrNlZaHC8hz4Tq4u6E7rvlw+L9//8dkMrmzlZ2K1bP1a5s67nPhqeNYPEG18X279YVvUuyV7lL6",
- "bUXdtZjtn4lVlPuL1e3tc2O3j8H+n+LWmTbJyD1AKw1mI95+j7eQPSa73ENjnx3a8J3qMpmQ18KlPilz",
- "KomQGUhX3mZeUkm5BsgmnlIxak3ZVA9pzoBr82DEgh0yUSwDGzE+LyVkJGdLrGgr4Rz94XF6fMs3INjO",
- "6NFr9rNl8q/oKkiHMK2uaS3cklHduaQrXzIIi2IIiT998w15MK5fLXluBkgqxMSY65KuRteo7auIbZCv",
- "eTOr/1ZnXBx7iOaoln5sdTbaTCH+1+bcX6zEbsndbeyeOOfOBp/aoBPqD1yCkY2aAyvY2YJCWOFmXYfz",
- "GinPi1BxFmdmGKoU+IxtA1tV0tHHZxu9t4f49vF/JVbSJqgd2QZGmKqDD2jLCHlG59xihNxfy0wa2Iyk",
- "WHqjkSAz0OnCBee2UB9hT76mQD9v2lRRct9SDe5iN6V1mN8RKx0OTBgSxE2i4Q5khIh/8ul9zWc2sxkq",
- "fL0IXzgVTVLM1xKryoi5YotMeT9+H8NrdnEnKJ/Xk3cFMkTLPuyetwjeDcEd5vitr1mFGHOL+DN4+vun",
- "ZEJeizpE3JVL+DOaHD/lzf6pF/RacLC2dSP5Wlq8NaNWYgeq8BEpPjeIfb9UiasvLYIc+OprG+WQH2zt",
- "s42yyJDb20z2RV7hP0TrGjduGbO2ydbEB/VoQ5izaWhzTDezS9/gK+ZG+Oln+LS5CY51PSwGD6nnM04s",
- "4PtlOphuxxLzQZVYuI8DxXO1D+ZGWlTuZ9H06lPIBZ+rz5MVbaKOOF4iVFJlsY+nqv/rnd3nmMnHPHmt",
- "56PL7aQYT8FWF/QF5l3iNQvh364PQs2WPhcnD2NWb5i7PH3w+PqmPwF5zlIgp7AshKSS5WvyM68qQV6F",
- "22Ei/irXmtcGR2svoLWpmQMsDRMWXZ4JNlzWPugVyz5uZ4ZBxr4d+SDjAR8M8wvSogAqL88At5uuTlsz",
- "Hr8IvYIb+eGr7FkRUAyKdnSM/4/RQL0ThruLmbv8Sm4B9Zm+HJtwLrtiNq6cY4wUIGaH5B2/T9SCPn34",
- "6LdHT7/yfz56+lWP5szM4xL0dHVn9UDmsx1miALti1YH7ldqr/B7eN27vdsmjkcsW0XzRdc1YTpZc51Y",
- "dkeRgq5708wXW2rahMPW9W2uP7Gh0my6iL6v/POnqnp7zJ9Vr2Cbfc+VgrmtZdMTNBHwGUNodVGbCuub",
- "69tskCZbZFkVErnux2kdXGAvOo882bpzblTQ1Tf1SE3wjQrcCzZNtNycTIk5zceBubuqI46+K2VRCKmr",
- "060mg8Q96DPbNaS9PsLdSZhLqU4XZXHwAf+D2bw+1gEHtnJnYOdzv8OqAMmM6IiFe9yvtvL+gbXtb5L+",
- "TmyLK96ULTHbehS0Mqz7dHPO30DMyCuWSnGECfTdJaTWSsOykxPQdf1tU0336IUleM44JEvBY5nqfsKv",
- "r/BjNEW/0DTv63xqPvb1bbHMJvwtsJrzDOGXV8XvZ/JAv5JiqbVaCeZw11XPLP3veAD9oVnztHuS1jzt",
- "Hr5G6bWenw8+NP50nj2upVqUOhMXQV98FloONcSoH2TQHq5Nr15KrUzUimSgDNF+eaqrAA+xE1N9jeQn",
- "C/Kk96Yo+4sqs2aMZy0iQTkzFecgVaXmkN4J51aj9efRaA3e9514rM3HuY2jlWq/EslrkYEdt5kCNxZE",
- "ykUGLm1oVxCpJLO4FsDfSnW71rsspeV8oUlZEC1iL8C6Y0JTy2RtcUa1rZqdbeVrsJwDobkEmq3JFIAT",
- "MTWLblYFJVShA71/Rjr5M16UrYarkCIFpSBLfNDsNtCqZKz46NQb8ISAI8DVLEQJMqPyysCenW+Fs0pg",
- "rsjdH39R924AXisKbkasdduNoLdyDXLSXhfqYdNvIrj25CHZUQnEiwao9RLLIgen94qgcCec9O5fG6LO",
- "Ll4dLagYYp+Y4v0kVyOgCtRPTO9XhbYsEnN/R8pG2q+nbImSGKdcKEgFz1R/cddtbBmLiARrUWYFASeM",
- "cWIcuOfB+ZIq/daZQMJaX0GxEjPFhmq0fYnyzci/VGnyO2On5j7kqlRVLn2n1ojX2+Kw2jDXa1hVc6EN",
- "yo9d6U20IKWCbSP3YSkY3yFLheVldWA8wlIi3cVhphPqFBRdVDaAqBGxCZAT36pRSK42bPQAwlSN6Kpm",
- "ZJNygqJZSouiwFp2Scmrfn1oOrGtj/TPddsucblyRnhvZwJUqNNykF9YzCoM5VhQRRwcZEnPnNpr7jJB",
- "RQp9sSUkaK5ONlG+OZYnplV4BLYe0rKYS5ph2VEaUaX8bD8T+3nTALjjnjyxpnMyhVm0NInZ9JqSZa+K",
- "qBpa4HgqJjxiCWhFUnMEZ1grxxOI671l5Ax66k+fBjUxXXOcK7pFfjxctt3qHrWUGcPsuCUHhNgx9CHw",
- "9qChGvnymMDOSa09aE/xd1BugkqM2H2SNai+JdTj77SAtjYvvL8aF0WLu7cYcJRr9nKxLWyk78TG9Idf",
- "ZKhf25j7CT3VmvrT4P03uczb9uCCMp3MhHRV9elMg4yo8lqFDijTPpLQmlW0cH4UBEdw16Ybx1Vnr9Nx",
- "OCZiQSC+didbRrL7mKm+E3JQOFDT6Y0yTUquWR6ERFcv5c9PX3irA7jVAdzqAG51ALc6gFsdwK0O4FYH",
- "cKsDuNUB3OoAbnUAf1kdwE3F9yVe4PBez1zwhMOcanYOVeDfbUqiP1U8THVVeZ0EajEuKNMuwSehXgzA",
- "L1cLB9RAc8QBy5HHFkL1Zk7CctBKlDIFkhoIGSdFTs3TAFa6SjfXTGTqUyu7gtCYG5UqePyInPxw5N32",
- "F869vNn27pFLUa70Ood7LqFDVbHVZ3YAbpDuEjtQfyX4tHQuSR/LgSiD3m+x9Qs4h1wUIK1HMNGyjGh8",
- "ToHmzx1utih8GgU3zWi/jxt6Joe2JS2Cwve4VqoItbEczXqZM5qr/oKZdrwlLWKZ4aqLz6qCkJs8E9m6",
- "dULMrh3gBjbPRu28zziV60jgTudEdEhDC8OvHGF1dVkf9x5i0iXaLplto7CYtC5BRc/xJiqPxlZUG9YZ",
- "yoYAzVp0Ei0o3Q4oGFUADnGANfTs94S8tf1uNoAdIXJHrGbmn43fYLNlxTSwrXlEONbzpUabe8RHTy+e",
- "/bEh7KxMgTCtiI9S2X69jEerxIw0B544BpRMRbZOGuxr1LiFMqaoUrCcbr+JQv7pciG7y8d82XxP3cw1",
- "8iJY3CaeHBLNKnEMuIc7rzUM5s0VtnBEx54DjH9qFt3HRkMQiONPMaVSuwLNjkyvnmZ9y/huGV9wGlsS",
- "AeMuqq/NRCafkPHJtSx5P8/7dgVpaYALT/Jd1M6jSQ5WumHXzGBazueY07ljozNLAxyPCX5DrNAudygX",
- "3I2C7OBVns+rppZqD9flLkEE210hyVyKsrhni1fxNRozlgXla2/yhUSxZZlbHNp0ePtltDbwrusIgOZY",
- "p/vr02q/8Sq/QHfrrtrm7xYt5IIqYvcXMlLyzEUOdcJzV3x4Pmk79OmK12x6Y0Zpu97I6ty8Q64Iv8su",
- "xKUycxcgE73i9kA1k77bMGB7cie3uWz/GtfGG5uHoYfBdkNaa4awp9tDBnwNr48gcUkdCteswGXrA/YF",
- "joRZTGzLvTqPdIZv+pAE1fmsjRTyglBfaCAVXGlZpvodp2ijCRY26fqXeG10P3977pvEzYQRK54b6h2n",
- "mIe+stxE+dwMImaK7wA8G1XlfA7K8MqQSGYA77hrxTgpuXlpiRlZslSKxIahmjNk5JOJbbmkazKjORoZ",
- "/wApyNTc7MGuW4Wx0izPnUOLmYaI2TtONcmBKk1eMcNlzXA+w1jlyQX6QsizCgvxpBZz4KCYSuLKl+/t",
- "V8wb4ZbvlXyosLSf63jv600Y4WFnWS/kxy8M3BRT5ORM6doHogP7tdm/l4wnUSI7XQBxLmFt2iJ3MYOM",
- "I6B7TeuQXsA7bm44LQhydaovRw5tM0/nLNrT0aKaxka0rEF+rYOeeHvhMiTCZG5NK3+iwMyADrz5Ejce",
- "K9S0935HM8rGopexry7PWE8j90hoKMKaF/eJa3HaAPnPm6P+/b60ZsNtGgtAnlnp+EPOqgWp9ujTas42",
- "upTUOeWWS8gY1ZCvSSEhBczkgr429YNzYmP2SbqgfI73jhTl3JWDtuNcgIQq/ZZ547WHiGf8WPEEBd2I",
- "48ARsco6T17m6GHBwxCd2NlyZ/Oo9Ki1CRWGPBsjx+F7M2bfK3I86pUSDVLPa98ui5zmGYlj4UKUeZao",
- "Mk0h5qFxHPWaqpbaFOBwLCu7uQEhI1kpbWVsQlNdYg2aKebFFLbkFOXr5vWPtbuEDPJnEkoUm3OqSwlj",
- "uzb0FJsCQYPjJOI+07rcG9d0sPM1Stuo2Ed5gdtjcHsM/nzHoHPpvHVEMmupEixlhAT4pyoMUWeOO3Il",
- "Ntq50f6sRSL61clus4W8TtXZ9Zdi+JTPi0+9mk/1WvFsXBGK9X5DTtDKpmgZA1WEacctp0DgnOYlclOX",
- "L9694SfktOa1lZd4qVwa03RBGXfZa6p4BoRDu1TL2ud23JdCk17oFXf6TMs2UZFpsAFpKZle40OGFuy3",
- "MzD/f29eArb+qH3jlDIfHY4WWheHBwdYsn8hlD4YfRyH31Tr4/sK/A/+eVJIdo4VfN5//P8BAAD//6G6",
- "O0DCSQEA",
+ "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",
+ "Ojxv3///AQAA//9KJ5oXyksBAA==",
}
// 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 b513cffac..ff9eb0f79 100644
--- a/daemon/algod/api/server/v2/generated/participating/private/routes.go
+++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go
@@ -158,173 +158,175 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
- "XwAAAP//qWbn/XHIAAA=",
+ "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",
+ "fvxYk8Cn+q5wpHD98fp/AgAA//+cgrUFs8oAAA==",
}
// GetSwagger returns the content of the embedded swagger specification file
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 f197fd7aa..7cf1dcd13 100644
--- a/daemon/algod/api/server/v2/generated/participating/public/routes.go
+++ b/daemon/algod/api/server/v2/generated/participating/public/routes.go
@@ -177,181 +177,184 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
- "dfgnpWTXWNbv/cf/CQAA///C44qjANcAAA==",
+ "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",
+ "QtkAAA==",
}
// 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 d9e53216d..08b14631c 100644
--- a/daemon/algod/api/server/v2/handlers.go
+++ b/daemon/algod/api/server/v2/handlers.go
@@ -920,15 +920,19 @@ func (v2 *Handlers) RawTransaction(ctx echo.Context) error {
// preEncodedSimulateTxnResult mirrors model.SimulateTransactionResult
type preEncodedSimulateTxnResult struct {
- Txn PreEncodedTxInfo `codec:"txn-result"`
- MissingSignature *bool `codec:"missing-signature,omitempty"`
+ Txn PreEncodedTxInfo `codec:"txn-result"`
+ MissingSignature *bool `codec:"missing-signature,omitempty"`
+ AppBudgetConsumed *uint64 `codec:"app-budget-consumed,omitempty"`
+ LogicSigBudgetConsumed *uint64 `codec:"logic-sig-budget-consumed,omitempty"`
}
// preEncodedSimulateTxnGroupResult mirrors model.SimulateTransactionGroupResult
type preEncodedSimulateTxnGroupResult struct {
- Txns []preEncodedSimulateTxnResult `codec:"txn-results"`
- FailureMessage *string `codec:"failure-message,omitempty"`
- FailedAt *[]uint64 `codec:"failed-at,omitempty"`
+ Txns []preEncodedSimulateTxnResult `codec:"txn-results"`
+ FailureMessage *string `codec:"failure-message,omitempty"`
+ FailedAt *[]uint64 `codec:"failed-at,omitempty"`
+ AppBudgetAdded *uint64 `codec:"app-budget-added,omitempty"`
+ AppBudgetConsumed *uint64 `codec:"app-budget-consumed,omitempty"`
}
// preEncodedSimulateResponse mirrors model.SimulateResponse
diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go
index b3232918e..19d4bea1b 100644
--- a/daemon/algod/api/server/v2/test/handlers_test.go
+++ b/daemon/algod/api/server/v2/test/handlers_test.go
@@ -82,6 +82,13 @@ func setupTestForMethodGet(t *testing.T, consensusUpgrade bool) (v2.Handlers, ec
return handler, c, rec, rootkeys, stxns, releasefunc
}
+func numOrNil(n uint64) *uint64 {
+ if n == 0 {
+ return nil
+ }
+ return &n
+}
+
func TestSimpleMockBuilding(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
@@ -960,23 +967,34 @@ int 1`,
clone[0]++
expectedFailedAt = &clone
}
+
+ var txnAppBudgetUsed []*uint64
+ appBudgetAdded := numOrNil(scenario.AppBudgetAdded)
+ appBudgetConsumed := numOrNil(scenario.AppBudgetConsumed)
+ for i := range scenario.TxnAppBudgetConsumed {
+ txnAppBudgetUsed = append(txnAppBudgetUsed, numOrNil(scenario.TxnAppBudgetConsumed[i]))
+ }
expectedBody := model.SimulateResponse{
Version: 1,
TxnGroups: []model.SimulateTransactionGroupResult{
{
- FailedAt: expectedFailedAt,
+ AppBudgetAdded: appBudgetAdded,
+ AppBudgetConsumed: appBudgetConsumed,
+ FailedAt: expectedFailedAt,
TxnResults: []model.SimulateTransactionResult{
{
TxnResult: makePendingTxnResponse(t, transactions.SignedTxnWithAD{
SignedTxn: stxns[0],
// expect no ApplyData info
}, responseFormat.handle),
+ AppBudgetConsumed: txnAppBudgetUsed[0],
},
{
TxnResult: makePendingTxnResponse(t, transactions.SignedTxnWithAD{
SignedTxn: stxns[1],
ApplyData: scenario.ExpectedSimulationAD,
}, responseFormat.handle),
+ AppBudgetConsumed: txnAppBudgetUsed[1],
},
},
},
diff --git a/daemon/algod/api/server/v2/utils.go b/daemon/algod/api/server/v2/utils.go
index b2b4e4a35..a78e33333 100644
--- a/daemon/algod/api/server/v2/utils.go
+++ b/daemon/algod/api/server/v2/utils.go
@@ -355,8 +355,10 @@ func ConvertInnerTxn(txn *transactions.SignedTxnWithAD) PreEncodedTxInfo {
func convertTxnResult(txnResult simulation.TxnResult) preEncodedSimulateTxnResult {
return preEncodedSimulateTxnResult{
- Txn: ConvertInnerTxn(&txnResult.Txn),
- MissingSignature: trueOrNil(txnResult.MissingSignature),
+ Txn: ConvertInnerTxn(&txnResult.Txn),
+ MissingSignature: trueOrNil(txnResult.MissingSignature),
+ AppBudgetConsumed: numOrNil(txnResult.AppBudgetConsumed),
+ LogicSigBudgetConsumed: numOrNil(txnResult.LogicSigBudgetConsumed),
}
}
@@ -367,8 +369,10 @@ func convertTxnGroupResult(txnGroupResult simulation.TxnGroupResult) preEncodedS
}
encoded := preEncodedSimulateTxnGroupResult{
- Txns: txnResults,
- FailureMessage: strOrNil(txnGroupResult.FailureMessage),
+ Txns: txnResults,
+ FailureMessage: strOrNil(txnGroupResult.FailureMessage),
+ AppBudgetAdded: numOrNil(txnGroupResult.AppBudgetAdded),
+ AppBudgetConsumed: numOrNil(txnGroupResult.AppBudgetConsumed),
}
if len(txnGroupResult.FailedAt) > 0 {
diff --git a/daemon/algod/server.go b/daemon/algod/server.go
index e116f206b..2283ef8e0 100644
--- a/daemon/algod/server.go
+++ b/daemon/algod/server.go
@@ -184,17 +184,17 @@ func (s *Server) Initialize(cfg config.Local, phonebookAddresses []string, genes
// if we have the telemetry enabled, we want to use it's sessionid as part of the
// collected metrics decorations.
- fmt.Fprintln(logWriter, "++++++++++++++++++++++++++++++++++++++++")
- fmt.Fprintln(logWriter, "Logging Starting")
+ s.log.Infoln("++++++++++++++++++++++++++++++++++++++++")
+ s.log.Infoln("Logging Starting")
if s.log.GetTelemetryUploadingEnabled() {
// May or may not be logging to node.log
- fmt.Fprintf(logWriter, "Telemetry Enabled: %s\n", s.log.GetTelemetryGUID())
- fmt.Fprintf(logWriter, "Session: %s\n", s.log.GetTelemetrySession())
+ s.log.Infof("Telemetry Enabled: %s\n", s.log.GetTelemetryGUID())
+ s.log.Infof("Session: %s\n", s.log.GetTelemetrySession())
} else {
// May or may not be logging to node.log
- fmt.Fprintln(logWriter, "Telemetry Disabled")
+ s.log.Infoln("Telemetry Disabled")
}
- fmt.Fprintln(logWriter, "++++++++++++++++++++++++++++++++++++++++")
+ s.log.Infoln("++++++++++++++++++++++++++++++++++++++++")
metricLabels := map[string]string{}
if s.log.GetTelemetryEnabled() {
diff --git a/data/transactions/logic/mocktracer/scenarios.go b/data/transactions/logic/mocktracer/scenarios.go
index f89a1d139..89fbdafe9 100644
--- a/data/transactions/logic/mocktracer/scenarios.go
+++ b/data/transactions/logic/mocktracer/scenarios.go
@@ -172,6 +172,9 @@ type TestScenario struct {
FailedAt []uint64
ExpectedEvents []Event
ExpectedSimulationAD transactions.ApplyData
+ AppBudgetAdded uint64
+ AppBudgetConsumed uint64
+ TxnAppBudgetConsumed []uint64
}
// TestScenarioGenerator is a function which instantiates a TestScenario
@@ -240,6 +243,9 @@ func GetTestScenarios() map[string]TestScenarioGenerator {
},
}),
ExpectedSimulationAD: expectedAD,
+ AppBudgetAdded: 2100,
+ AppBudgetConsumed: 35,
+ TxnAppBudgetConsumed: []uint64{0, 35},
}
}
@@ -292,6 +298,9 @@ func GetTestScenarios() map[string]TestScenarioGenerator {
},
}),
ExpectedSimulationAD: expectedAD,
+ AppBudgetAdded: 700,
+ AppBudgetConsumed: 4,
+ TxnAppBudgetConsumed: []uint64{0, 4},
}
}
scenarios[beforeInnersName] = beforeInners
@@ -341,6 +350,9 @@ func GetTestScenarios() map[string]TestScenarioGenerator {
},
}),
ExpectedSimulationAD: expectedAD,
+ AppBudgetAdded: 1400,
+ AppBudgetConsumed: 15,
+ TxnAppBudgetConsumed: []uint64{0, 15},
}
}
scenarios[firstInnerName] = firstInner
@@ -391,6 +403,9 @@ func GetTestScenarios() map[string]TestScenarioGenerator {
},
}),
ExpectedSimulationAD: expectedAD,
+ AppBudgetAdded: 1400,
+ AppBudgetConsumed: 19,
+ TxnAppBudgetConsumed: []uint64{0, 19},
}
}
scenarios[betweenInnersName] = betweenInners
@@ -449,6 +464,9 @@ func GetTestScenarios() map[string]TestScenarioGenerator {
},
}),
ExpectedSimulationAD: expectedAD,
+ AppBudgetAdded: 2100,
+ AppBudgetConsumed: 32,
+ TxnAppBudgetConsumed: []uint64{0, 32},
}
}
scenarios[secondInnerName] = secondInner
@@ -510,6 +528,9 @@ func GetTestScenarios() map[string]TestScenarioGenerator {
},
}),
ExpectedSimulationAD: expectedAD,
+ AppBudgetAdded: 2100,
+ AppBudgetConsumed: 32,
+ TxnAppBudgetConsumed: []uint64{0, 32},
}
}
scenarios[thirdInnerName] = thirdInner
@@ -567,6 +588,9 @@ func GetTestScenarios() map[string]TestScenarioGenerator {
},
}),
ExpectedSimulationAD: expectedAD,
+ AppBudgetAdded: 2100,
+ AppBudgetConsumed: 35,
+ TxnAppBudgetConsumed: []uint64{0, 35},
}
}
scenarios[afterInnersName] = afterInners
diff --git a/data/txHandler.go b/data/txHandler.go
index 1d3578d3c..3ad271670 100644
--- a/data/txHandler.go
+++ b/data/txHandler.go
@@ -122,7 +122,6 @@ type TxHandler struct {
net network.GossipNode
msgCache *txSaltedCache
txCanonicalCache *digestCache
- cacheConfig txHandlerConfig
ctx context.Context
ctxCancel context.CancelFunc
streamVerifier *execpool.StreamToBatch
@@ -142,12 +141,6 @@ type TxHandlerOpts struct {
Config config.Local
}
-// txHandlerConfig is a subset of tx handler related options from config.Local
-type txHandlerConfig struct {
- enableFilteringRawMsg bool
- enableFilteringCanonical bool
-}
-
// MakeTxHandler makes a new handler for transaction messages
func MakeTxHandler(opts TxHandlerOpts) (*TxHandler, error) {
@@ -174,13 +167,19 @@ func MakeTxHandler(opts TxHandlerOpts) (*TxHandler, error) {
backlogQueue: 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 execpool.InputJob),
streamVerifierDropped: make(chan *verify.UnverifiedTxnSigJob),
}
+ // use defaultBacklogSize = approx number of txns in a full block as a parameter for the dedup cache size
+ defaultBacklogSize := config.GetDefaultLocal().TxBacklogSize
+ if opts.Config.TxFilterRawMsgEnabled() {
+ handler.msgCache = makeSaltedCache(2 * defaultBacklogSize)
+ }
+ if opts.Config.TxFilterCanonicalEnabled() {
+ handler.txCanonicalCache = makeDigestCache(2 * defaultBacklogSize)
+ }
+
if opts.Config.EnableTxBacklogRateLimiting {
rateLimiter := util.NewElasticRateLimiter(
txBacklogSize,
@@ -191,7 +190,7 @@ func MakeTxHandler(opts TxHandlerOpts) (*TxHandler, error) {
handler.erl = rateLimiter
}
- // prepare the transaction stream verifer
+ // prepare the transaction stream verifier
var err error
txnElementProcessor, err := verify.MakeSigVerifyJobProcessor(handler.ledger, handler.ledger.VerifiedTransactionCache(),
handler.postVerificationQueue, handler.streamVerifierDropped)
@@ -219,7 +218,9 @@ func (handler *TxHandler) droppedTxnWatcher() {
// 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)
+ if handler.msgCache != nil {
+ handler.msgCache.Start(handler.ctx, 60*time.Second)
+ }
handler.net.RegisterHandlers([]network.TaggedMessageHandler{
{Tag: protocol.TxnTag, MessageHandler: network.HandlerFunc(handler.processIncomingTxn)},
})
@@ -240,7 +241,9 @@ func (handler *TxHandler) Stop() {
}
handler.backlogWg.Wait()
handler.streamVerifier.WaitForStop()
- handler.msgCache.WaitForStop()
+ if handler.msgCache != nil {
+ handler.msgCache.WaitForStop()
+ }
}
func reencode(stxns []transactions.SignedTxn) []byte {
@@ -497,11 +500,11 @@ func (handler *TxHandler) postProcessCheckedTxn(wi *txBacklogMsg) {
}
func (handler *TxHandler) deleteFromCaches(msgKey *crypto.Digest, canonicalKey *crypto.Digest) {
- if handler.cacheConfig.enableFilteringCanonical && canonicalKey != nil {
+ if handler.txCanonicalCache != nil && canonicalKey != nil {
handler.txCanonicalCache.Delete(canonicalKey)
}
- if handler.cacheConfig.enableFilteringRawMsg && msgKey != nil {
+ if handler.msgCache != nil && msgKey != nil {
handler.msgCache.DeleteByKey(msgKey)
}
}
@@ -561,7 +564,7 @@ func (handler *TxHandler) dedupCanonical(ntx int, unverifiedTxGroup []transactio
func (handler *TxHandler) processIncomingTxn(rawmsg network.IncomingMessage) network.OutgoingMessage {
var msgKey *crypto.Digest
var isDup bool
- if handler.cacheConfig.enableFilteringRawMsg {
+ if handler.msgCache != nil {
// check for duplicate messages
// this helps against relaying duplicates
if msgKey, isDup = handler.msgCache.CheckAndPut(rawmsg.Data); isDup {
@@ -632,7 +635,7 @@ func (handler *TxHandler) processIncomingTxn(rawmsg network.IncomingMessage) net
}
var canonicalKey *crypto.Digest
- if handler.cacheConfig.enableFilteringCanonical {
+ if handler.txCanonicalCache != nil {
if canonicalKey, isDup = handler.dedupCanonical(ntx, unverifiedTxGroup, consumed); isDup {
transactionMessagesDupCanonical.Inc(nil)
return network.OutgoingMessage{Action: network.Ignore}
@@ -653,10 +656,10 @@ func (handler *TxHandler) processIncomingTxn(rawmsg network.IncomingMessage) net
transactionMessagesDroppedFromBacklog.Inc(nil)
// additionally, remove the txn from duplicate caches to ensure it can be re-submitted
- if canonicalKey != nil {
+ if handler.txCanonicalCache != nil && canonicalKey != nil {
handler.txCanonicalCache.Delete(canonicalKey)
}
- if msgKey != nil {
+ if handler.msgCache != nil && msgKey != nil {
handler.msgCache.DeleteByKey(msgKey)
}
}
diff --git a/data/txHandler_test.go b/data/txHandler_test.go
index f0ea2d5b4..f51290a37 100644
--- a/data/txHandler_test.go
+++ b/data/txHandler_test.go
@@ -55,13 +55,19 @@ import (
)
// txHandler uses config values to determine backlog size. Tests should use a static value
-var txBacklogSize = 26000
+var txBacklogSize = config.GetDefaultLocal().TxBacklogSize
// mock sender is used to implement OnClose, since TXHandlers expect to use Senders and ERL Clients
type mockSender struct{}
func (m mockSender) OnClose(func()) {}
+// txHandlerConfig is a subset of tx handler related options from config.Local
+type txHandlerConfig struct {
+ enableFilteringRawMsg bool
+ enableFilteringCanonical bool
+}
+
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)
@@ -529,12 +535,25 @@ func BenchmarkTxHandlerIncDeDup(b *testing.B) {
numPoolWorkers := runtime.NumCPU()
dupFactor := test.dupFactor
avgDelay := test.workerDelay / time.Duration(numPoolWorkers)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
- handler := makeTestTxHandlerOrphaned(txBacklogSize)
+ var handler *TxHandler
if test.firstLevelOnly {
- handler.cacheConfig = txHandlerConfig{enableFilteringRawMsg: true, enableFilteringCanonical: false}
+ handler = makeTestTxHandlerOrphanedWithContext(
+ ctx, txBacklogSize, txBacklogSize,
+ txHandlerConfig{enableFilteringRawMsg: true, enableFilteringCanonical: false}, 0,
+ )
} else if !test.dedup {
- handler.cacheConfig = txHandlerConfig{}
+ handler = makeTestTxHandlerOrphanedWithContext(
+ ctx, txBacklogSize, 0,
+ txHandlerConfig{}, 0,
+ )
+ } else {
+ handler = makeTestTxHandlerOrphanedWithContext(
+ ctx, txBacklogSize, txBacklogSize,
+ txHandlerConfig{enableFilteringRawMsg: true, enableFilteringCanonical: true}, 0,
+ )
}
// prepare tx groups
@@ -783,12 +802,17 @@ func makeTestTxHandlerOrphanedWithContext(ctx context.Context, backlogSize int,
cacheSize = txBacklogSize
}
handler := &TxHandler{
- backlogQueue: make(chan *txBacklogMsg, backlogSize),
- msgCache: makeSaltedCache(cacheSize),
- txCanonicalCache: makeDigestCache(cacheSize),
- cacheConfig: txHandlerConfig,
+ backlogQueue: make(chan *txBacklogMsg, backlogSize),
+ }
+
+ if txHandlerConfig.enableFilteringRawMsg {
+ handler.msgCache = makeSaltedCache(cacheSize)
+ handler.msgCache.Start(ctx, refreshInterval)
}
- handler.msgCache.Start(ctx, refreshInterval)
+ if txHandlerConfig.enableFilteringCanonical {
+ handler.txCanonicalCache = makeDigestCache(cacheSize)
+ }
+
return handler
}
@@ -892,6 +916,8 @@ 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())
+ defer cancelFunc()
+
handler := makeTestTxHandlerOrphanedWithContext(ctx, txBacklogSize, txBacklogSize, txHandlerConfig{true, true}, 10*time.Millisecond)
var action network.OutgoingMessage
@@ -907,12 +933,15 @@ func TestTxHandlerProcessIncomingCacheRotation(t *testing.T) {
msg = <-handler.backlogQueue
require.Equal(t, 1, len(msg.unverifiedTxGroup))
require.Equal(t, stxns1[0], msg.unverifiedTxGroup[0])
- cancelFunc()
})
t.Run("manual", func(t *testing.T) {
// double enqueue a single txn message, ensure it discarded
- handler := makeTestTxHandlerOrphaned(txBacklogSize)
+ ctx, cancelFunc := context.WithCancel(context.Background())
+ defer cancelFunc()
+
+ handler := makeTestTxHandlerOrphanedWithContext(ctx, txBacklogSize, txBacklogSize, txHandlerConfig{true, true}, 10*time.Millisecond)
+
var action network.OutgoingMessage
var msg *txBacklogMsg
@@ -1684,17 +1713,16 @@ func runHandlerBenchmarkWithBacklog(b *testing.B, txGen txGenIf, tps int, useBac
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
+ cfg.TxIncomingFilteringFlags = 0
+ handler, err := makeTestTxHandler(ledger, cfg)
+ require.NoError(b, 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())
diff --git a/docker/README.md b/docker/README.md
index 5db6b20a1..0ea18b0c5 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -22,15 +22,16 @@ The following environment variables can be supplied. Except when noted, it is po
| Variable | Description |
| -------- | ----------- |
-| NETWORK | Leave blank for a private network, otherwise specify one of mainnet, betanet, testnet, or devnet. Only used during a data directory initialization. |
-| FAST_CATCHUP | If set to 1 on a public network, attempt to start fast-catchup during initial config. |
-| TELEMETRY_NAME| If set on a public network, telemetry is reported with this name. |
-| DEV_MODE | If set to 1 on a private network, enable dev mode. Only used during data directory initialization. |
-| NUM_ROUNDS | If set on a private network, override default of 30000 participation keys. |
-| TOKEN | If set, overrides the REST API token. |
-| ADMIN_TOKEN | If set, overrides the REST API admin token. |
-| KMD_TOKEN | If set along with `START_KMD`, override the KMD REST API token. |
-| START_KMD | When set to 1, start kmd service with no timeout. THIS SHOULD NOT BE USED IN PRODUCTION. |
+| NETWORK | Leave blank for a private network, otherwise specify one of mainnet, betanet, testnet, or devnet. Only used during a data directory initialization. |
+| FAST_CATCHUP | If set to 1 on a public network, attempt to start fast-catchup during initial config. |
+| TELEMETRY_NAME | If set on a public network, telemetry is reported with this name. |
+| DEV_MODE | If set to 1 on a private network, enable dev mode. Only used during data directory initialization. |
+| NUM_ROUNDS | If set on a private network, override default of 30000 participation keys. |
+| TOKEN | If set, overrides the REST API token. |
+| ADMIN_TOKEN | If set, overrides the REST API admin token. |
+| KMD_TOKEN | If set along with `START_KMD`, override the KMD REST API token. |
+| START_KMD | When set to 1, start kmd service with no timeout. THIS SHOULD NOT BE USED IN PRODUCTION. |
+| PEER_ADDRESS | If set, override phonebook with peer ip:port (or semicolon separated list: ip:port;ip:port;ip:port...) |
### Special Files
@@ -42,8 +43,7 @@ Configuration can be modified by specifying certain files. These can be changed
| /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/algorand/template.json` for overriding the private network topology.
+ | /etc/algorand/template.json | Override default private network topology. One of the nodes in the template must be named "data".|
## Example Configuration
diff --git a/docker/files/run/followermode_template.json b/docker/files/run/followermode_template.json
new file mode 100644
index 000000000..ed074587e
--- /dev/null
+++ b/docker/files/run/followermode_template.json
@@ -0,0 +1,51 @@
+{
+ "Genesis": {
+ "ConsensusProtocol": "future",
+ "NetworkName": "followermodenet",
+ "FirstPartKeyRound": 0,
+ "LastPartKeyRound": NUM_ROUNDS,
+ "Wallets": [
+ {
+ "Name": "Wallet1",
+ "Stake": 40,
+ "Online": true
+ },
+ {
+ "Name": "Wallet2",
+ "Stake": 40,
+ "Online": true
+ },
+ {
+ "Name": "Wallet3",
+ "Stake": 20,
+ "Online": true
+ }
+ ],
+ "DevMode": true
+ },
+ "Nodes": [
+ {
+ "Name": "data",
+ "IsRelay": true,
+ "Wallets": [
+ {
+ "Name": "Wallet1",
+ "ParticipationOnly": false
+ },
+ {
+ "Name": "Wallet2",
+ "ParticipationOnly": false
+ },
+ {
+ "Name": "Wallet3",
+ "ParticipationOnly": false
+ }
+ ]
+ },
+ {
+ "Name": "follower",
+ "IsRelay": false,
+ "ConfigJSONOverride": "{\"EnableFollowMode\":true,\"EndpointAddress\":\"0.0.0.0:8081\"}"
+ }
+ ]
+}
diff --git a/docker/files/run/run.sh b/docker/files/run/run.sh
index bd4e79fe0..f9caa9881 100755
--- a/docker/files/run/run.sh
+++ b/docker/files/run/run.sh
@@ -35,8 +35,13 @@ function start_public_network() {
catchup &
fi
- # redirect output to stdout
- algod -o
+ if [ "$PEER_ADDRESS" != "" ]; then
+ printf "$PEER_ADDRESS"
+ algod -o -p $PEER_ADDRESS
+ else
+ # redirect output to stdout
+ algod -o
+ fi
}
function configure_data_dir() {
@@ -59,7 +64,9 @@ function configure_data_dir() {
# check for token overrides
if [ "$TOKEN" != "" ]; then
- echo "$TOKEN" >algod.token
+ for dir in ${ALGORAND_DATA}/../*/; do
+ echo "$TOKEN" > "$dir/algod.token"
+ done
fi
if [ "$ADMIN_TOKEN" != "" ]; then
echo "$ADMIN_TOKEN" >algod.admin.token
@@ -142,8 +149,12 @@ function start_private_network() {
function start_new_private_network() {
local TEMPLATE="template.json"
- if [ "$DEV_MODE" = "1" ]; then
- TEMPLATE="devmode_template.json"
+ if [ -f "/etc/algorand/template.json" ]; then
+ cp /etc/algorand/template.json "/node/run/$TEMPLATE"
+ else
+ if [ "$DEV_MODE" = "1" ]; then
+ TEMPLATE="devmode_template.json"
+ fi
fi
sed -i "s/NUM_ROUNDS/${NUM_ROUNDS:-30000}/" "/node/run/$TEMPLATE"
goal network create --noclean -n dockernet -r "${ALGORAND_DATA}/.." -t "/node/run/$TEMPLATE"
diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go
index cecd7a5b2..596b85c72 100644
--- a/ledger/acctupdates_test.go
+++ b/ledger/acctupdates_test.go
@@ -21,6 +21,7 @@ import (
"context"
"errors"
"fmt"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-deadlock"
"os"
"runtime"
@@ -36,7 +37,6 @@ 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/internal"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store/trackerdb"
"github.com/algorand/go-algorand/ledger/store/trackerdb/sqlitedriver"
@@ -199,7 +199,7 @@ func (ml *mockLedgerForTracker) addMockBlock(be blockEntry, delta ledgercore.Sta
return nil
}
-func (ml *mockLedgerForTracker) trackerEvalVerified(blk bookkeeping.Block, accUpdatesLedger internal.LedgerForEvaluator) (ledgercore.StateDelta, error) {
+func (ml *mockLedgerForTracker) trackerEvalVerified(blk bookkeeping.Block, accUpdatesLedger eval.LedgerForEvaluator) (ledgercore.StateDelta, error) {
ml.mu.RLock()
defer ml.mu.RUnlock()
diff --git a/ledger/archival_test.go b/ledger/archival_test.go
index f30821a35..de483e227 100644
--- a/ledger/archival_test.go
+++ b/ledger/archival_test.go
@@ -37,7 +37,7 @@ 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/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store/blockdb"
"github.com/algorand/go-algorand/ledger/store/trackerdb"
@@ -68,7 +68,7 @@ func (wl *wrappedLedger) BlockHdr(rnd basics.Round) (bookkeeping.BlockHeader, er
return wl.l.BlockHdr(rnd)
}
-func (wl *wrappedLedger) trackerEvalVerified(blk bookkeeping.Block, accUpdatesLedger internal.LedgerForEvaluator) (ledgercore.StateDelta, error) {
+func (wl *wrappedLedger) trackerEvalVerified(blk bookkeeping.Block, accUpdatesLedger eval.LedgerForEvaluator) (ledgercore.StateDelta, error) {
return wl.l.trackerEvalVerified(blk, accUpdatesLedger)
}
diff --git a/ledger/catchpointtracker_test.go b/ledger/catchpointtracker_test.go
index b994f25c2..d24efac19 100644
--- a/ledger/catchpointtracker_test.go
+++ b/ledger/catchpointtracker_test.go
@@ -558,6 +558,7 @@ func TestReproducibleCatchpointLabels(t *testing.T) {
if (uint64(i) >= cfg.MaxAcctLookback) && (uint64(i)-cfg.MaxAcctLookback > protoParams.CatchpointLookback) && ((uint64(i)-cfg.MaxAcctLookback)%cfg.CatchpointInterval == 0) {
ml.trackers.waitAccountsWriting()
catchpointLabels[i] = ct.GetLastCatchpointLabel()
+ require.NotEmpty(t, catchpointLabels[i])
require.NotEqual(t, lastCatchpointLabel, catchpointLabels[i])
lastCatchpointLabel = catchpointLabels[i]
ledgerHistory[i] = ml.fork(t)
diff --git a/ledger/double_test.go b/ledger/double_test.go
index bbc5e9520..86ae3b660 100644
--- a/ledger/double_test.go
+++ b/ledger/double_test.go
@@ -24,7 +24,7 @@ import (
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/txntest"
- "github.com/algorand/go-algorand/ledger/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/protocol"
"github.com/stretchr/testify/require"
@@ -47,7 +47,7 @@ type DoubleLedger struct {
generator *Ledger
validator *Ledger
- eval *internal.BlockEvaluator
+ eval *eval.BlockEvaluator
}
func (dl DoubleLedger) Close() {
@@ -62,7 +62,7 @@ func NewDoubleLedger(t *testing.T, balances bookkeeping.GenesisBalances, cv prot
return DoubleLedger{t, g, v, nil}
}
-func (dl *DoubleLedger) beginBlock() *internal.BlockEvaluator {
+func (dl *DoubleLedger) beginBlock() *eval.BlockEvaluator {
dl.eval = nextBlock(dl.t, dl.generator)
return dl.eval
}
@@ -202,7 +202,7 @@ func checkBlock(t *testing.T, checkLedger *Ledger, vb *ledgercore.ValidatedBlock
// require.Equal(t, vb.Delta().Accts, cb.Delta().Accts)
}
-func nextCheckBlock(t testing.TB, ledger *Ledger, rs bookkeeping.RewardsState) *internal.BlockEvaluator {
+func nextCheckBlock(t testing.TB, ledger *Ledger, rs bookkeeping.RewardsState) *eval.BlockEvaluator {
rnd := ledger.Latest()
hdr, err := ledger.BlockHdr(rnd)
require.NoError(t, err)
@@ -211,7 +211,7 @@ func nextCheckBlock(t testing.TB, ledger *Ledger, rs bookkeeping.RewardsState) *
nextHdr.RewardsState = rs
// follow nextBlock, which does this for determinism
nextHdr.TimeStamp = hdr.TimeStamp + 1
- eval, err := internal.StartEvaluator(ledger, nextHdr, internal.EvaluatorOptions{
+ eval, err := eval.StartEvaluator(ledger, nextHdr, eval.EvaluatorOptions{
Generate: false,
Validate: true, // Do the complete checks that a new txn would be subject to
})
diff --git a/ledger/internal/appcow.go b/ledger/eval/appcow.go
index 5b53687e4..0e2270452 100644
--- a/ledger/internal/appcow.go
+++ b/ledger/eval/appcow.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"fmt"
@@ -29,7 +29,7 @@ import (
"github.com/algorand/go-algorand/protocol"
)
-//msgp: ignore storageAction
+//msgp:ignore storageAction
type storageAction uint64
const (
@@ -74,6 +74,7 @@ func (vd valueDelta) serialize() (vdelta basics.ValueDelta, ok bool) {
}
// stateDelta is similar to basics.StateDelta but stores both values before and after change
+//
//msgp:ignore stateDelta
type stateDelta map[string]valueDelta
diff --git a/ledger/internal/appcow_test.go b/ledger/eval/appcow_test.go
index 9e7247958..2c46d02c0 100644
--- a/ledger/internal/appcow_test.go
+++ b/ledger/eval/appcow_test.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"fmt"
diff --git a/ledger/internal/applications.go b/ledger/eval/applications.go
index f0466b7d4..7263425e3 100644
--- a/ledger/internal/applications.go
+++ b/ledger/eval/applications.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"fmt"
diff --git a/ledger/internal/assetcow.go b/ledger/eval/assetcow.go
index 3813dad7c..50b710675 100644
--- a/ledger/internal/assetcow.go
+++ b/ledger/eval/assetcow.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"github.com/algorand/go-algorand/data/basics"
diff --git a/ledger/internal/cow.go b/ledger/eval/cow.go
index e34942d63..66824ce97 100644
--- a/ledger/internal/cow.go
+++ b/ledger/eval/cow.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"errors"
diff --git a/ledger/internal/cow_creatables.go b/ledger/eval/cow_creatables.go
index d43135cf8..1c5651a18 100644
--- a/ledger/internal/cow_creatables.go
+++ b/ledger/eval/cow_creatables.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"fmt"
diff --git a/ledger/internal/cow_test.go b/ledger/eval/cow_test.go
index e94dd0c4b..f5e4fe25a 100644
--- a/ledger/internal/cow_test.go
+++ b/ledger/eval/cow_test.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"errors"
diff --git a/ledger/internal/eval.go b/ledger/eval/eval.go
index df64a19e2..62533fe14 100644
--- a/ledger/internal/eval.go
+++ b/ledger/eval/eval.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"context"
@@ -30,7 +30,7 @@ import (
"github.com/algorand/go-algorand/data/transactions/logic"
"github.com/algorand/go-algorand/data/transactions/verify"
"github.com/algorand/go-algorand/ledger/apply"
- "github.com/algorand/go-algorand/ledger/internal/prefetcher"
+ "github.com/algorand/go-algorand/ledger/eval/prefetcher"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
diff --git a/ledger/internal/eval_test.go b/ledger/eval/eval_test.go
index 82792e621..57860c1e9 100644
--- a/ledger/internal/eval_test.go
+++ b/ledger/eval/eval_test.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"bytes"
diff --git a/ledger/internal/evalindexer.go b/ledger/eval/evalindexer.go
index babf6c87d..af3a5546c 100644
--- a/ledger/internal/evalindexer.go
+++ b/ledger/eval/evalindexer.go
@@ -14,7 +14,7 @@
// 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 internal
+package eval
import (
"fmt"
diff --git a/ledger/internal/prefetcher/error.go b/ledger/eval/prefetcher/error.go
index 77c1cb999..77c1cb999 100644
--- a/ledger/internal/prefetcher/error.go
+++ b/ledger/eval/prefetcher/error.go
diff --git a/ledger/internal/prefetcher/prefetcher.go b/ledger/eval/prefetcher/prefetcher.go
index e00d78f70..e00d78f70 100644
--- a/ledger/internal/prefetcher/prefetcher.go
+++ b/ledger/eval/prefetcher/prefetcher.go
diff --git a/ledger/internal/prefetcher/prefetcher_alignment_test.go b/ledger/eval/prefetcher/prefetcher_alignment_test.go
index 9ad45bd16..efb9e683b 100644
--- a/ledger/internal/prefetcher/prefetcher_alignment_test.go
+++ b/ledger/eval/prefetcher/prefetcher_alignment_test.go
@@ -31,8 +31,8 @@ import (
"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/ledger/internal"
- "github.com/algorand/go-algorand/ledger/internal/prefetcher"
+ "github.com/algorand/go-algorand/ledger/eval"
+ "github.com/algorand/go-algorand/ledger/eval/prefetcher"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/test/partitiontest"
@@ -297,7 +297,7 @@ func runEval(t *testing.T, l *prefetcherAlignmentTestLedger, txn transactions.Tr
require.NoError(t, err)
block := bookkeeping.MakeBlock(genesisBlockHeader)
- eval, err := internal.StartEvaluator(l, block.BlockHeader, internal.EvaluatorOptions{})
+ eval, err := eval.StartEvaluator(l, block.BlockHeader, eval.EvaluatorOptions{})
require.NoError(t, err)
err = eval.TransactionGroup(makeGroupFromTxn(txn))
diff --git a/ledger/internal/prefetcher/prefetcher_test.go b/ledger/eval/prefetcher/prefetcher_test.go
index b9c1d80eb..2eff5f344 100644
--- a/ledger/internal/prefetcher/prefetcher_test.go
+++ b/ledger/eval/prefetcher/prefetcher_test.go
@@ -28,7 +28,7 @@ import (
"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/ledger/internal/prefetcher"
+ "github.com/algorand/go-algorand/ledger/eval/prefetcher"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/test/partitiontest"
diff --git a/ledger/internal/prefetcher/prefetcher_whitebox_test.go b/ledger/eval/prefetcher/prefetcher_whitebox_test.go
index 6a8738b48..6a8738b48 100644
--- a/ledger/internal/prefetcher/prefetcher_whitebox_test.go
+++ b/ledger/eval/prefetcher/prefetcher_whitebox_test.go
diff --git a/ledger/evalbench_test.go b/ledger/evalbench_test.go
index 9b827d83f..407c6f67f 100644
--- a/ledger/evalbench_test.go
+++ b/ledger/evalbench_test.go
@@ -37,7 +37,7 @@ 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/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
ledgertesting "github.com/algorand/go-algorand/ledger/testing"
"github.com/algorand/go-algorand/logging"
@@ -491,7 +491,7 @@ func benchmarkBlockEvaluator(b *testing.B, inMem bool, withCrypto bool, proto pr
if withCrypto {
_, err = l2.Validate(context.Background(), validatedBlock.Block(), backlogPool)
} else {
- _, err = internal.Eval(context.Background(), l2, validatedBlock.Block(), false, nil, nil)
+ _, err = eval.Eval(context.Background(), l2, validatedBlock.Block(), false, nil, nil)
}
require.NoError(b, err)
}
diff --git a/ledger/evalindexer.go b/ledger/evalindexer.go
index d8af6539e..5ac28ce81 100644
--- a/ledger/evalindexer.go
+++ b/ledger/evalindexer.go
@@ -25,7 +25,7 @@ import (
"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/ledger/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
)
@@ -238,9 +238,9 @@ func makeIndexerLedgerConnector(il indexerLedgerForEval, genesisHash crypto.Dige
func EvalForIndexer(il indexerLedgerForEval, block *bookkeeping.Block, proto config.ConsensusParams, resources EvalForIndexerResources) (ledgercore.StateDelta, []transactions.SignedTxnInBlock, error) {
ilc := makeIndexerLedgerConnector(il, block.GenesisHash(), proto, block.Round()-1, resources)
- eval, err := internal.StartEvaluator(
+ eval, err := eval.StartEvaluator(
ilc, block.BlockHeader,
- internal.EvaluatorOptions{
+ eval.EvaluatorOptions{
PaysetHint: len(block.Payset),
ProtoParams: &proto,
Generate: false,
diff --git a/ledger/fullblock_perf_test.go b/ledger/fullblock_perf_test.go
index 9bea4a00a..0de160970 100644
--- a/ledger/fullblock_perf_test.go
+++ b/ledger/fullblock_perf_test.go
@@ -37,7 +37,7 @@ import (
"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/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
@@ -53,7 +53,7 @@ type benchConfig struct {
acctToApp map[basics.Address]map[basics.AppIndex]struct{}
l0 *Ledger
l1 *Ledger
- eval *internal.BlockEvaluator
+ eval *eval.BlockEvaluator
numPay uint64
numAst uint64
numApp uint64
@@ -119,7 +119,7 @@ func setupEnv(b *testing.B, numAccts int) (bc *benchConfig) {
require.NoError(b, err)
newBlk := bookkeeping.MakeBlock(blk.BlockHeader)
- eval, err := l0.StartEvaluator(newBlk.BlockHeader, 5000, 0)
+ blockEvaluator, err := l0.StartEvaluator(newBlk.BlockHeader, 5000, 0)
require.NoError(b, err)
bc = &benchConfig{
@@ -132,7 +132,7 @@ func setupEnv(b *testing.B, numAccts int) (bc *benchConfig) {
acctToApp: acctToApp,
l0: l0,
l1: l1,
- eval: eval,
+ eval: blockEvaluator,
}
// start the ledger with a pool of accounts
@@ -144,7 +144,7 @@ func setupEnv(b *testing.B, numAccts int) (bc *benchConfig) {
addBlock(bc)
vc := verify.GetMockedCache(true)
for _, blk := range bc.blocks {
- _, err := internal.Eval(context.Background(), bc.l1, blk, true, vc, nil)
+ _, err := eval.Eval(context.Background(), bc.l1, blk, true, vc, nil)
require.NoError(b, err)
err = bc.l1.AddBlock(blk, cert)
require.NoError(b, err)
@@ -424,7 +424,7 @@ func benchmarkBlockValidationMix(b *testing.B, newAcctProb, payProb, astProb flo
tt := time.Now()
b.ResetTimer()
for _, blk := range bc.blocks {
- _, err := internal.Eval(context.Background(), bc.l1, blk, true, vc, nil)
+ _, err := eval.Eval(context.Background(), bc.l1, blk, true, vc, nil)
require.NoError(b, err)
err = bc.l1.AddBlock(blk, cert)
require.NoError(b, err)
diff --git a/ledger/ledger.go b/ledger/ledger.go
index 82fd24cd0..8ad2cb9ce 100644
--- a/ledger/ledger.go
+++ b/ledger/ledger.go
@@ -33,7 +33,7 @@ import (
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/verify"
"github.com/algorand/go-algorand/ledger/apply"
- "github.com/algorand/go-algorand/ledger/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store/blockdb"
"github.com/algorand/go-algorand/ledger/store/trackerdb"
@@ -680,7 +680,7 @@ func (l *Ledger) BlockCert(rnd basics.Round) (blk bookkeeping.Block, cert agreem
func (l *Ledger) AddBlock(blk bookkeeping.Block, cert agreement.Certificate) error {
// passing nil as the executionPool is ok since we've asking the evaluator to skip verification.
- updates, err := internal.Eval(context.Background(), l, blk, false, l.verifiedTxnCache, nil)
+ updates, err := eval.Eval(context.Background(), l, blk, false, l.verifiedTxnCache, nil)
if err != nil {
if errNSBE, ok := err.(ledgercore.ErrNonSequentialBlockEval); ok && errNSBE.EvaluatorRound <= errNSBE.LatestRound {
return ledgercore.BlockInLedgerError{
@@ -807,9 +807,9 @@ func (l *Ledger) trackerLog() logging.Logger {
// trackerEvalVerified is used by the accountUpdates to reconstruct the ledgercore.StateDelta from a given block during it's loadFromDisk execution.
// when this function is called, the trackers mutex is expected already to be taken. The provided accUpdatesLedger would allow the
// evaluator to shortcut the "main" ledger ( i.e. this struct ) and avoid taking the trackers lock a second time.
-func (l *Ledger) trackerEvalVerified(blk bookkeeping.Block, accUpdatesLedger internal.LedgerForEvaluator) (ledgercore.StateDelta, error) {
+func (l *Ledger) trackerEvalVerified(blk bookkeeping.Block, accUpdatesLedger eval.LedgerForEvaluator) (ledgercore.StateDelta, error) {
// passing nil as the executionPool is ok since we've asking the evaluator to skip verification.
- return internal.Eval(context.Background(), accUpdatesLedger, blk, false, l.verifiedTxnCache, nil)
+ return eval.Eval(context.Background(), accUpdatesLedger, blk, false, l.verifiedTxnCache, nil)
}
// IsWritingCatchpointDataFile returns true when a catchpoint file is being generated.
@@ -833,9 +833,9 @@ func (l *Ledger) VerifiedTransactionCache() verify.VerifiedTransactionCache {
// provides a cap on the size of a single generated block size, when a non-zero value is passed.
// If a value of zero or less is passed to maxTxnBytesPerBlock, the consensus MaxTxnBytesPerBlock would
// be used instead.
-func (l *Ledger) StartEvaluator(hdr bookkeeping.BlockHeader, paysetHint, maxTxnBytesPerBlock int) (*internal.BlockEvaluator, error) {
- return internal.StartEvaluator(l, hdr,
- internal.EvaluatorOptions{
+func (l *Ledger) StartEvaluator(hdr bookkeeping.BlockHeader, paysetHint, maxTxnBytesPerBlock int) (*eval.BlockEvaluator, error) {
+ return eval.StartEvaluator(l, hdr,
+ eval.EvaluatorOptions{
PaysetHint: paysetHint,
Generate: true,
Validate: true,
@@ -853,7 +853,7 @@ func (l *Ledger) FlushCaches() {
// not a valid block (e.g., it has duplicate transactions, overspends some
// account, etc).
func (l *Ledger) Validate(ctx context.Context, blk bookkeeping.Block, executionPool execpool.BacklogPool) (*ledgercore.ValidatedBlock, error) {
- delta, err := internal.Eval(ctx, l, blk, true, l.verifiedTxnCache, executionPool)
+ delta, err := eval.Eval(ctx, l, blk, true, l.verifiedTxnCache, executionPool)
if err != nil {
return nil, err
}
@@ -863,11 +863,11 @@ func (l *Ledger) Validate(ctx context.Context, blk bookkeeping.Block, executionP
}
// DebuggerLedger defines the minimal set of method required for creating a debug balances.
-type DebuggerLedger = internal.LedgerForCowBase
+type DebuggerLedger = eval.LedgerForCowBase
// MakeDebugBalances creates a ledger suitable for dryrun and debugger
func MakeDebugBalances(l DebuggerLedger, round basics.Round, proto protocol.ConsensusVersion, prevTimestamp int64) apply.Balances {
- return internal.MakeDebugBalances(l, round, proto, prevTimestamp)
+ return eval.MakeDebugBalances(l, round, proto, prevTimestamp)
}
var ledgerInitblocksdbCount = metrics.NewCounter("ledger_initblocksdb_count", "calls")
diff --git a/ledger/ledger_perf_test.go b/ledger/ledger_perf_test.go
index 2c5cb8ec5..b2f3b2437 100644
--- a/ledger/ledger_perf_test.go
+++ b/ledger/ledger_perf_test.go
@@ -36,7 +36,7 @@ import (
"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/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
@@ -319,7 +319,7 @@ func benchmarkFullBlocks(params testParams, b *testing.B) {
vc := verify.GetMockedCache(true)
b.ResetTimer()
for _, blk := range blocks {
- _, err = internal.Eval(context.Background(), l1, blk, true, vc, nil)
+ _, err = eval.Eval(context.Background(), l1, blk, true, vc, nil)
require.NoError(b, err)
err = l1.AddBlock(blk, cert)
require.NoError(b, err)
diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go
index 2b83f0df6..9dab45207 100644
--- a/ledger/ledger_test.go
+++ b/ledger/ledger_test.go
@@ -21,6 +21,7 @@ import (
"context"
"errors"
"fmt"
+ "github.com/algorand/go-algorand/ledger/eval"
"math/rand"
"os"
"path/filepath"
@@ -41,7 +42,6 @@ import (
"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/internal"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store/trackerdb"
ledgertesting "github.com/algorand/go-algorand/ledger/testing"
@@ -3187,7 +3187,7 @@ func TestLedgerSPVerificationTracker(t *testing.T) {
// This implementation is an easy way to feed the delta, which the state proof verification tracker relies on,
// to the ledger.
- delta, err := internal.Eval(context.Background(), l, blk, false, l.verifiedTxnCache, nil)
+ delta, err := eval.Eval(context.Background(), l, blk, false, l.verifiedTxnCache, nil)
require.NoError(t, err)
delta.ModStateProofNextRound = stateProofReceived.StateProofNextRound
vb := ledgercore.MakeValidatedBlock(blk, delta)
diff --git a/ledger/simple_test.go b/ledger/simple_test.go
index d4e44f0c3..8a41cb4a6 100644
--- a/ledger/simple_test.go
+++ b/ledger/simple_test.go
@@ -28,7 +28,7 @@ import (
"github.com/algorand/go-algorand/data/bookkeeping"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/txntest"
- "github.com/algorand/go-algorand/ledger/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
@@ -58,14 +58,14 @@ func newSimpleLedgerFull(t testing.TB, balances bookkeeping.GenesisBalances, cv
}
// nextBlock begins evaluation of a new block, after ledger creation or endBlock()
-func nextBlock(t testing.TB, ledger *Ledger) *internal.BlockEvaluator {
+func nextBlock(t testing.TB, ledger *Ledger) *eval.BlockEvaluator {
rnd := ledger.Latest()
hdr, err := ledger.BlockHdr(rnd)
require.NoError(t, err)
nextHdr := bookkeeping.MakeBlock(hdr).BlockHeader
nextHdr.TimeStamp = hdr.TimeStamp + 1 // ensure deterministic tests
- eval, err := internal.StartEvaluator(ledger, nextHdr, internal.EvaluatorOptions{
+ eval, err := eval.StartEvaluator(ledger, nextHdr, eval.EvaluatorOptions{
Generate: true,
Validate: true, // Do the complete checks that a new txn would be subject to
})
@@ -73,7 +73,7 @@ func nextBlock(t testing.TB, ledger *Ledger) *internal.BlockEvaluator {
return eval
}
-func fillDefaults(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator, txn *txntest.Txn) {
+func fillDefaults(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, txn *txntest.Txn) {
if txn.GenesisHash.IsZero() && ledger.GenesisProto().SupportGenesisHash {
txn.GenesisHash = ledger.GenesisHash()
}
@@ -84,14 +84,14 @@ func fillDefaults(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator, t
txn.FillDefaults(ledger.GenesisProto())
}
-func txns(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator, txns ...*txntest.Txn) {
+func txns(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, txns ...*txntest.Txn) {
t.Helper()
for _, txn1 := range txns {
txn(t, ledger, eval, txn1)
}
}
-func txn(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator, txn *txntest.Txn, problem ...string) {
+func txn(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, txn *txntest.Txn, problem ...string) {
t.Helper()
fillDefaults(t, ledger, eval, txn)
err := eval.Transaction(txn.SignedTxn(), transactions.ApplyData{})
@@ -106,7 +106,7 @@ func txn(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator, txn *txnte
require.True(t, len(problem) == 0 || problem[0] == "")
}
-func txgroup(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator, txns ...*txntest.Txn) error {
+func txgroup(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, txns ...*txntest.Txn) error {
t.Helper()
for _, txn := range txns {
fillDefaults(t, ledger, eval, txn)
@@ -117,7 +117,7 @@ func txgroup(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator, txns .
}
// endBlock completes the block being created, returns the ValidatedBlock for inspection
-func endBlock(t testing.TB, ledger *Ledger, eval *internal.BlockEvaluator) *ledgercore.ValidatedBlock {
+func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator) *ledgercore.ValidatedBlock {
validatedBlock, err := eval.GenerateBlock()
require.NoError(t, err)
err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{})
diff --git a/ledger/simulation/simulation_eval_test.go b/ledger/simulation/simulation_eval_test.go
index 0af8b5ae0..134fdd233 100644
--- a/ledger/simulation/simulation_eval_test.go
+++ b/ledger/simulation/simulation_eval_test.go
@@ -20,6 +20,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
+ "strings"
"testing"
"github.com/algorand/go-algorand/crypto"
@@ -541,21 +542,25 @@ btoi`)
name string
arguments [][]byte
expectedError string
+ cost uint64
}{
{
name: "approval",
arguments: [][]byte{{1}},
expectedError: "", // no error
+ cost: 2,
},
{
name: "rejection",
arguments: [][]byte{{0}},
expectedError: "rejected by logic",
+ cost: 2,
},
{
name: "error",
arguments: [][]byte{},
expectedError: "rejected by logic err=cannot load arg[0] of 0",
+ cost: 1,
},
}
@@ -595,6 +600,7 @@ int 1`,
expectedSuccess := len(testCase.expectedError) == 0
var expectedAppCallAD transactions.ApplyData
expectedFailedAt := simulation.TxnPath{1}
+ var AppBudgetConsumed, AppBudgetAdded uint64
if expectedSuccess {
expectedAppCallAD = transactions.ApplyData{
ApplicationID: 2,
@@ -603,6 +609,8 @@ int 1`,
},
}
expectedFailedAt = nil
+ AppBudgetConsumed = 3
+ AppBudgetAdded = 700
}
return simulationTestCase{
@@ -619,9 +627,13 @@ int 1`,
Txn: transactions.SignedTxnWithAD{
ApplyData: expectedAppCallAD,
},
+ AppBudgetConsumed: AppBudgetConsumed,
+ LogicSigBudgetConsumed: testCase.cost,
},
},
- FailedAt: expectedFailedAt,
+ FailedAt: expectedFailedAt,
+ AppBudgetAdded: AppBudgetAdded,
+ AppBudgetConsumed: AppBudgetConsumed,
},
},
WouldSucceed: expectedSuccess,
@@ -698,7 +710,8 @@ int 0
},
},
},
- MissingSignature: !signed,
+ MissingSignature: !signed,
+ AppBudgetConsumed: 5,
},
{
Txn: transactions.SignedTxnWithAD{
@@ -708,9 +721,12 @@ int 0
},
},
},
- MissingSignature: !signed,
+ MissingSignature: !signed,
+ AppBudgetConsumed: 6,
},
},
+ AppBudgetAdded: 1400,
+ AppBudgetConsumed: 11,
},
},
WouldSucceed: signed,
@@ -771,10 +787,13 @@ int 0
},
},
},
- MissingSignature: !signed,
+ MissingSignature: !signed,
+ AppBudgetConsumed: 3,
},
},
- FailedAt: simulation.TxnPath{0},
+ FailedAt: simulation.TxnPath{0},
+ AppBudgetAdded: 700,
+ AppBudgetConsumed: 3,
},
},
WouldSucceed: false,
@@ -835,10 +854,13 @@ int 0
},
},
},
- MissingSignature: !signed,
+ MissingSignature: !signed,
+ AppBudgetConsumed: 3,
},
},
- FailedAt: simulation.TxnPath{0},
+ FailedAt: simulation.TxnPath{0},
+ AppBudgetAdded: 700,
+ AppBudgetConsumed: 3,
},
},
WouldSucceed: false,
@@ -849,6 +871,237 @@ int 0
}
}
+func TestAppCallOverBudget(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ // Transaction group has a cost of 4 + 1398
+ expensiveAppSource := `#pragma version 6
+ txn ApplicationID // [appId]
+ bz end // []
+` + strings.Repeat(`int 1
+ pop
+`, 697) + `end:
+ int 1`
+
+ simulationTest(t, func(accounts []simulationtesting.Account, txnInfo simulationtesting.TxnInfo) simulationTestCase {
+ sender := accounts[0]
+ receiver := accounts[1]
+
+ futureAppID := basics.AppIndex(1)
+ // App create with cost 4
+ createTxn := txnInfo.NewTxn(txntest.Txn{
+ Type: protocol.ApplicationCallTx,
+ Sender: sender.Addr,
+ ApplicationID: 0,
+ ApprovalProgram: expensiveAppSource,
+ ClearStateProgram: `#pragma version 6
+int 0
+`,
+ })
+ // App call with cost 1398 - will cause a budget exceeded error,
+ // but will only report a cost up to 1396.
+ expensiveTxn := txnInfo.NewTxn(txntest.Txn{
+ Type: protocol.ApplicationCallTx,
+ Sender: sender.Addr,
+ ApplicationID: futureAppID,
+ Accounts: []basics.Address{receiver.Addr},
+ })
+
+ txntest.Group(&createTxn, &expensiveTxn)
+
+ signedCreateTxn := createTxn.Txn().Sign(sender.Sk)
+ signedExpensiveTxn := expensiveTxn.Txn().Sign(sender.Sk)
+
+ return simulationTestCase{
+ input: []transactions.SignedTxn{signedCreateTxn, signedExpensiveTxn},
+ expectedError: "dynamic cost budget exceeded",
+ expected: simulation.Result{
+ Version: 1,
+ LastRound: txnInfo.LatestRound(),
+ TxnGroups: []simulation.TxnGroupResult{
+ {
+ Txns: []simulation.TxnResult{
+ {
+ Txn: transactions.SignedTxnWithAD{
+ ApplyData: transactions.ApplyData{
+ ApplicationID: futureAppID,
+ },
+ },
+ AppBudgetConsumed: 4,
+ },
+ {
+ AppBudgetConsumed: 1396,
+ },
+ },
+ FailedAt: simulation.TxnPath{1},
+ AppBudgetAdded: 1400,
+ AppBudgetConsumed: 1400,
+ },
+ },
+ WouldSucceed: false,
+ },
+ }
+ })
+}
+
+func TestLogicSigOverBudget(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ op, err := logic.AssembleString(`#pragma version 6
+` + strings.Repeat(`byte "a"
+keccak256
+pop
+`, 200) + `int 1`)
+ require.NoError(t, err)
+ program := logic.Program(op.Program)
+ lsigAddr := basics.Address(crypto.HashObj(&program))
+
+ simulationTest(t, func(accounts []simulationtesting.Account, txnInfo simulationtesting.TxnInfo) simulationTestCase {
+ sender := accounts[0]
+
+ payTxn := txnInfo.NewTxn(txntest.Txn{
+ Type: protocol.PaymentTx,
+ Sender: sender.Addr,
+ Receiver: lsigAddr,
+ Amount: 1_000_000,
+ })
+ appCallTxn := txnInfo.NewTxn(txntest.Txn{
+ Type: protocol.ApplicationCallTx,
+ Sender: lsigAddr,
+ ApprovalProgram: `#pragma version 8
+byte "hello"
+log
+int 1`,
+ ClearStateProgram: `#pragma version 8
+int 1`,
+ })
+
+ txntest.Group(&payTxn, &appCallTxn)
+
+ signedPayTxn := payTxn.Txn().Sign(sender.Sk)
+ signedAppCallTxn := appCallTxn.SignedTxn()
+ signedAppCallTxn.Lsig = transactions.LogicSig{
+ Logic: program,
+ }
+
+ var expectedAppCallAD transactions.ApplyData
+ expectedFailedAt := simulation.TxnPath{1}
+
+ // Opcode cost exceeded, but report current cost of LogicSig before it went over the limit.
+ return simulationTestCase{
+ input: []transactions.SignedTxn{signedPayTxn, signedAppCallTxn},
+ expectedError: "dynamic cost budget exceeded",
+ expected: simulation.Result{
+ Version: 1,
+ LastRound: txnInfo.LatestRound(),
+ TxnGroups: []simulation.TxnGroupResult{
+ {
+ Txns: []simulation.TxnResult{
+ {},
+ {
+ Txn: transactions.SignedTxnWithAD{
+ ApplyData: expectedAppCallAD,
+ },
+ AppBudgetConsumed: 0,
+ LogicSigBudgetConsumed: 19934,
+ },
+ },
+ FailedAt: expectedFailedAt,
+ AppBudgetAdded: 0,
+ AppBudgetConsumed: 0,
+ },
+ },
+ WouldSucceed: false,
+ },
+ }
+ })
+}
+
+func TestAppAtBudget(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ // Transaction has a cost of 700 and invokes an inner transaction
+ exactly700AndCallInner := fmt.Sprintf(`#pragma version 6
+pushint 1
+cover 0 // This is a noop, just to fix an odd number of ops
+%s
+itxn_begin
+int appl
+itxn_field TypeEnum
+byte 0x068101
+dup
+itxn_field ClearStateProgram
+itxn_field ApprovalProgram
+itxn_submit
+`, strings.Repeat(`pushint 1
+pop
+`, 345))
+
+ simulationTest(t, func(accounts []simulationtesting.Account, txnInfo simulationtesting.TxnInfo) simulationTestCase {
+ sender := accounts[0]
+
+ futureAppID := basics.AppIndex(2)
+ // fund outer app
+ fund := txnInfo.NewTxn(txntest.Txn{
+ Type: protocol.PaymentTx,
+ Sender: sender.Addr,
+ Receiver: futureAppID.Address(),
+ Amount: 401_000,
+ })
+ // create app
+ appCall := txnInfo.NewTxn(txntest.Txn{
+ Type: protocol.ApplicationCallTx,
+ Sender: sender.Addr,
+ ApprovalProgram: exactly700AndCallInner,
+ ClearStateProgram: `#pragma version 6
+int 1`,
+ })
+
+ txntest.Group(&fund, &appCall)
+
+ signedFundTxn := fund.Txn().Sign(sender.Sk)
+ signedAppCall := appCall.Txn().Sign(sender.Sk)
+
+ return simulationTestCase{
+ input: []transactions.SignedTxn{signedFundTxn, signedAppCall},
+ expected: simulation.Result{
+ Version: 1,
+ LastRound: txnInfo.LatestRound(),
+ TxnGroups: []simulation.TxnGroupResult{
+ {
+ Txns: []simulation.TxnResult{
+ {},
+ {
+ Txn: transactions.SignedTxnWithAD{
+ ApplyData: transactions.ApplyData{
+ ApplicationID: futureAppID,
+ EvalDelta: transactions.EvalDelta{
+ InnerTxns: []transactions.SignedTxnWithAD{
+ {
+ ApplyData: transactions.ApplyData{
+ ApplicationID: futureAppID + 1,
+ },
+ },
+ },
+ },
+ },
+ },
+ AppBudgetConsumed: 701,
+ },
+ },
+ AppBudgetAdded: 1400,
+ AppBudgetConsumed: 701,
+ },
+ },
+ WouldSucceed: true,
+ },
+ }
+ })
+}
+
func TestSignatureCheck(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
@@ -1013,18 +1266,23 @@ int 1`,
ApplicationID: futureAppID,
},
},
- MissingSignature: !signed,
+ MissingSignature: !signed,
+ AppBudgetConsumed: 4,
},
{
- MissingSignature: !signed,
+ MissingSignature: !signed,
+ AppBudgetConsumed: 10,
},
{
MissingSignature: !signed,
},
{
- MissingSignature: !signed,
+ MissingSignature: !signed,
+ AppBudgetConsumed: 10,
},
},
+ AppBudgetAdded: 2100,
+ AppBudgetConsumed: 24,
},
},
WouldSucceed: signed,
@@ -1275,10 +1533,13 @@ int 1`,
},
},
},
- MissingSignature: true,
+ AppBudgetConsumed: 27,
+ MissingSignature: true,
},
},
- FailedAt: simulation.TxnPath{2, 0, 0},
+ AppBudgetAdded: 2100,
+ AppBudgetConsumed: 27,
+ FailedAt: simulation.TxnPath{2, 0, 0},
},
},
WouldSucceed: false,
@@ -1363,10 +1624,13 @@ int 1`,
},
},
},
- MissingSignature: true,
+ AppBudgetConsumed: 23,
+ MissingSignature: true,
},
},
- FailedAt: simulation.TxnPath{1, 1},
+ AppBudgetAdded: 2100,
+ AppBudgetConsumed: 23,
+ FailedAt: simulation.TxnPath{1, 1},
},
},
WouldSucceed: false,
@@ -1446,10 +1710,13 @@ int 1`,
},
},
},
- MissingSignature: true,
+ AppBudgetConsumed: 17,
+ MissingSignature: true,
},
},
- FailedAt: simulation.TxnPath{1, 1},
+ AppBudgetAdded: 2100,
+ AppBudgetConsumed: 17,
+ FailedAt: simulation.TxnPath{1, 1},
},
},
WouldSucceed: false,
@@ -1506,13 +1773,18 @@ func TestMockTracerScenarios(t *testing.T) {
LastRound: txnInfo.LatestRound(),
TxnGroups: []simulation.TxnGroupResult{
{
- FailedAt: expectedFailedAt,
+ AppBudgetAdded: scenario.AppBudgetAdded,
+ AppBudgetConsumed: scenario.AppBudgetConsumed,
+ FailedAt: expectedFailedAt,
Txns: []simulation.TxnResult{
- {},
+ {
+ AppBudgetConsumed: scenario.TxnAppBudgetConsumed[0],
+ },
{
Txn: transactions.SignedTxnWithAD{
ApplyData: scenario.ExpectedSimulationAD,
},
+ AppBudgetConsumed: scenario.TxnAppBudgetConsumed[1],
},
},
},
diff --git a/ledger/simulation/simulator.go b/ledger/simulation/simulator.go
index 5ae93b019..e2b8ffd11 100644
--- a/ledger/simulation/simulator.go
+++ b/ledger/simulation/simulator.go
@@ -218,5 +218,12 @@ func (s Simulator) Simulate(txgroup []transactions.SignedTxn) (Result, error) {
simulatorTracer.result.WouldSucceed = false
}
+ // Update total cost by aggregating individual txn costs
+ totalCost := uint64(0)
+ for _, txn := range simulatorTracer.result.TxnGroups[0].Txns {
+ totalCost += txn.AppBudgetConsumed
+ }
+ simulatorTracer.result.TxnGroups[0].AppBudgetConsumed = totalCost
+
return *simulatorTracer.result, nil
}
diff --git a/ledger/simulation/simulator_test.go b/ledger/simulation/simulator_test.go
index 856fabe25..76ae0ef1b 100644
--- a/ledger/simulation/simulator_test.go
+++ b/ledger/simulation/simulator_test.go
@@ -26,7 +26,7 @@ import (
"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/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
simulationtesting "github.com/algorand/go-algorand/ledger/simulation/testing"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/test/partitiontest"
@@ -71,7 +71,7 @@ func TestNonOverridenDataLedgerMethodsUseRoundParameter(t *testing.T) {
}
methodExistsInEvalLedger := func(methodName string) bool {
- evalLedgerType := reflect.TypeOf((*internal.LedgerForEvaluator)(nil)).Elem()
+ evalLedgerType := reflect.TypeOf((*eval.LedgerForEvaluator)(nil)).Elem()
for i := 0; i < evalLedgerType.NumMethod(); i++ {
if evalLedgerType.Method(i).Name == methodName {
return true
diff --git a/ledger/simulation/trace.go b/ledger/simulation/trace.go
index d9c2e5f6a..6313e0539 100644
--- a/ledger/simulation/trace.go
+++ b/ledger/simulation/trace.go
@@ -30,8 +30,10 @@ type TxnPath []uint64
// TxnResult contains the simulation result for a single transaction
type TxnResult struct {
- Txn transactions.SignedTxnWithAD
- MissingSignature bool
+ Txn transactions.SignedTxnWithAD
+ MissingSignature bool
+ AppBudgetConsumed uint64
+ LogicSigBudgetConsumed uint64
}
// TxnGroupResult contains the simulation result for a single transaction group
@@ -41,6 +43,15 @@ type TxnGroupResult struct {
// FailedAt is the path to the txn that failed inside of this group
FailedAt TxnPath
+
+ // AppBudgetAdded is the total opcode budget for this group
+ AppBudgetAdded uint64
+
+ // AppBudgetConsumed is the total opcode cost used for this group
+ AppBudgetConsumed uint64
+
+ // FeeCredit is the fees left over after covering fees for this group
+ FeeCredit uint64
}
func makeTxnGroupResult(txgroup []transactions.SignedTxn) TxnGroupResult {
diff --git a/ledger/simulation/tracer.go b/ledger/simulation/tracer.go
index 7f5840753..9a42e617c 100644
--- a/ledger/simulation/tracer.go
+++ b/ledger/simulation/tracer.go
@@ -130,8 +130,17 @@ func (tracer *evalTracer) BeforeTxnGroup(ep *logic.EvalParams) {
if ep.GetCaller() != nil {
// If this is an inner txn group, save the txns
tracer.populateInnerTransactions(ep.TxnGroup)
+ tracer.result.TxnGroups[0].AppBudgetAdded += uint64(ep.Proto.MaxAppProgramCost)
}
tracer.cursorEvalTracer.BeforeTxnGroup(ep)
+
+ // Currently only supports one (first) txn group
+ if ep.PooledApplicationBudget != nil && tracer.result.TxnGroups[0].AppBudgetAdded == 0 {
+ tracer.result.TxnGroups[0].AppBudgetAdded = uint64(*ep.PooledApplicationBudget)
+ }
+ if ep.FeeCredit != nil {
+ tracer.result.TxnGroups[0].FeeCredit = *ep.FeeCredit
+ }
}
func (tracer *evalTracer) AfterTxnGroup(ep *logic.EvalParams, evalError error) {
@@ -186,8 +195,14 @@ func (tracer *evalTracer) AfterOpcode(cx *logic.EvalContext, evalError error) {
func (tracer *evalTracer) AfterProgram(cx *logic.EvalContext, evalError error) {
if cx.RunMode() != logic.ModeApp {
- // do nothing for LogicSig programs
+ // Report cost for LogicSig program and exit
+ tracer.result.TxnGroups[0].Txns[cx.GroupIndex()].LogicSigBudgetConsumed = uint64(cx.Cost())
return
}
+
+ // Report cost of this program.
+ // If it is an inner app call, roll up its cost to the top level transaction.
+ tracer.result.TxnGroups[0].Txns[tracer.relativeCursor[0]].AppBudgetConsumed += uint64(cx.Cost())
+
tracer.handleError(evalError)
}
diff --git a/ledger/store/merkle_committer.go b/ledger/store/merkle_committer.go
new file mode 100644
index 000000000..bc7502dac
--- /dev/null
+++ b/ledger/store/merkle_committer.go
@@ -0,0 +1,75 @@
+// Copyright (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 "database/sql"
+
+// MerkleCommitter allows storing and loading merkletrie pages from a sqlite database.
+//
+//msgp:ignore MerkleCommitter
+type MerkleCommitter struct {
+ tx *sql.Tx
+ deleteStmt *sql.Stmt
+ insertStmt *sql.Stmt
+ selectStmt *sql.Stmt
+}
+
+// MakeMerkleCommitter creates a MerkleCommitter object that implements the merkletrie.Committer interface allowing storing and loading
+// merkletrie pages from a sqlite database.
+func MakeMerkleCommitter(tx *sql.Tx, staging bool) (mc *MerkleCommitter, err error) {
+ mc = &MerkleCommitter{tx: tx}
+ accountHashesTable := "accounthashes"
+ if staging {
+ accountHashesTable = "catchpointaccounthashes"
+ }
+ mc.deleteStmt, err = tx.Prepare("DELETE FROM " + accountHashesTable + " WHERE id=?")
+ if err != nil {
+ return nil, err
+ }
+ mc.insertStmt, err = tx.Prepare("INSERT OR REPLACE INTO " + accountHashesTable + "(id, data) VALUES(?, ?)")
+ if err != nil {
+ return nil, err
+ }
+ mc.selectStmt, err = tx.Prepare("SELECT data FROM " + accountHashesTable + " WHERE id = ?")
+ if err != nil {
+ return nil, err
+ }
+ return mc, nil
+}
+
+// StorePage is the merkletrie.Committer interface implementation, stores a single page in a sqlite database table.
+func (mc *MerkleCommitter) StorePage(page uint64, content []byte) error {
+ if len(content) == 0 {
+ _, err := mc.deleteStmt.Exec(page)
+ return err
+ }
+ _, err := mc.insertStmt.Exec(page, content)
+ return err
+}
+
+// LoadPage is the merkletrie.Committer interface implementation, load a single page from a sqlite database table.
+func (mc *MerkleCommitter) LoadPage(page uint64) (content []byte, err error) {
+ err = mc.selectStmt.QueryRow(page).Scan(&content)
+ if err == sql.ErrNoRows {
+ content = nil
+ err = nil
+ return
+ } else if err != nil {
+ return nil, err
+ }
+ return content, nil
+}
diff --git a/ledger/store/trackerdb/sqlitedriver/accountsV2.go b/ledger/store/trackerdb/sqlitedriver/accountsV2.go
index 78083c95a..3ffb06999 100644
--- a/ledger/store/trackerdb/sqlitedriver/accountsV2.go
+++ b/ledger/store/trackerdb/sqlitedriver/accountsV2.go
@@ -651,7 +651,12 @@ func (w *accountsV2Writer) TxtailNewRound(ctx context.Context, baseRound basics.
return err
}
-// OnlineAccountsDelete deleted entries with updRound <= expRound
+// OnlineAccountsDelete cleans up the Online Accounts table to prune expired entires.
+// it will delete entries with an updRound <= expRound
+// EXCEPT, it will not delete the *latest* entry for an account, no matter how old.
+// this is so that accounts whos last update is before expRound still maintain an Online Account Balance
+// After this cleanup runs, accounts in this table will have either one entry (if all entries besides the latest are expired),
+// or will have more than one entry (if multiple entries are not yet expired).
func (w *accountsV2Writer) OnlineAccountsDelete(forgetBefore basics.Round) (err error) {
rows, err := w.e.Query("SELECT rowid, address, updRound, data FROM onlineaccounts WHERE updRound < ? ORDER BY address, updRound DESC", forgetBefore)
if err != nil {
diff --git a/ledger/tracker.go b/ledger/tracker.go
index 3e229d1f0..f03c5b806 100644
--- a/ledger/tracker.go
+++ b/ledger/tracker.go
@@ -28,7 +28,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/internal"
+ "github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"
"github.com/algorand/go-algorand/ledger/store/trackerdb"
"github.com/algorand/go-algorand/logging"
@@ -136,7 +136,7 @@ type ledgerForTracker interface {
trackerDB() trackerdb.TrackerStore
blockDB() db.Pair
trackerLog() logging.Logger
- trackerEvalVerified(bookkeeping.Block, internal.LedgerForEvaluator) (ledgercore.StateDelta, error)
+ trackerEvalVerified(bookkeeping.Block, eval.LedgerForEvaluator) (ledgercore.StateDelta, error)
Latest() basics.Round
Block(basics.Round) (bookkeeping.Block, error)
diff --git a/libgoal/libgoal.go b/libgoal/libgoal.go
index 4756aa155..5951793de 100644
--- a/libgoal/libgoal.go
+++ b/libgoal/libgoal.go
@@ -504,11 +504,10 @@ func (c *Client) signAndBroadcastTransactionWithWallet(walletHandle, pw []byte,
//
// validRounds | lastValid | result (lastValid)
// -------------------------------------------------
-// 0 | 0 | firstValid + maxTxnLife
-// 0 | N | lastValid
-// M | 0 | first + validRounds - 1
-// M | M | error
-//
+// 0 | 0 | firstValid + maxTxnLife
+// 0 | N | lastValid
+// M | 0 | first + validRounds - 1
+// M | M | error
func (c *Client) ComputeValidityRounds(firstValid, lastValid, validRounds uint64) (first, last, latest uint64, err error) {
params, err := c.cachedSuggestedParams()
if err != nil {
@@ -1270,6 +1269,16 @@ func (c *Client) Dryrun(data []byte) (resp model.DryrunResponse, err error) {
return
}
+// TransactionSimulation takes raw transaction or raw transaction group, and returns relevant simulation results.
+func (c *Client) TransactionSimulation(data []byte) (resp model.SimulateResponse, err error) {
+ algod, err := c.ensureAlgodClient()
+ if err != nil {
+ return
+ }
+ resp, err = algod.SimulateRawTransaction(data)
+ return
+}
+
// TransactionProof returns a Merkle proof for a transaction in a block.
func (c *Client) TransactionProof(txid string, round uint64, hashType crypto.HashType) (resp model.TransactionProofResponse, err error) {
algod, err := c.ensureAlgodClient()
diff --git a/network/wsNetwork.go b/network/wsNetwork.go
index 0280952d1..7339bbde6 100644
--- a/network/wsNetwork.go
+++ b/network/wsNetwork.go
@@ -101,8 +101,9 @@ const slowWritingPeerMonitorInterval = 5 * time.Second
// to the log file. Note that the log file itself would also json-encode these before placing them in the log file.
const unprintableCharacterGlyph = "▯"
-// match config.PublicAddress to this string to automatically set PublicAddress from Address()
-const autoconfigPublicAddress = "auto"
+// testingPublicAddress is used in identity exchange tests for a predictable
+// PublicAddress (which will match HTTP Listener's Address) in tests only.
+const testingPublicAddress = "testing"
var networkIncomingConnections = metrics.MakeGauge(metrics.NetworkIncomingConnections)
var networkOutgoingConnections = metrics.MakeGauge(metrics.NetworkOutgoingConnections)
@@ -842,8 +843,8 @@ func (wn *WebsocketNetwork) Start() {
wn.scheme = "http"
}
- // if PublicAddress set to automatic, pull the name from Address()
- if wn.config.PublicAddress == autoconfigPublicAddress {
+ // if PublicAddress set to testing, pull the name from Address()
+ if wn.config.PublicAddress == testingPublicAddress {
addr, ok := wn.Address()
if ok {
url, err := url.Parse(addr)
diff --git a/network/wsNetwork_test.go b/network/wsNetwork_test.go
index c08042c46..4f7d01c95 100644
--- a/network/wsNetwork_test.go
+++ b/network/wsNetwork_test.go
@@ -1131,25 +1131,25 @@ func TestGetPeers(t *testing.T) {
assert.Equal(t, expectAddrs, peerAddrs)
}
-// confirms that if the config PublicAddress is set to "auto",
+// confirms that if the config PublicAddress is set to "testing",
// PublicAddress is loaded when possible with the value of Address()
-func TestAutoPublicAddress(t *testing.T) {
+func TestTestingPublicAddress(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()
netA := makeTestWebsocketNode(t)
- netA.config.PublicAddress = "auto"
+ netA.config.PublicAddress = "testing"
netA.config.GossipFanout = 1
netA.Start()
time.Sleep(100 * time.Millisecond)
- // check that "auto" has been overloaded
+ // check that "testing" has been overloaded
addr, ok := netA.Address()
addr = hostAndPort(addr)
require.True(t, ok)
- require.NotEqual(t, "auto", netA.PublicAddress())
+ require.NotEqual(t, "testing", netA.PublicAddress())
require.Equal(t, addr, netA.PublicAddress())
}
@@ -1231,12 +1231,12 @@ func TestPeeringWithIdentityChallenge(t *testing.T) {
netA := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netA"})
netA.identityTracker = newMockIdentityTracker(netA.identityTracker)
- netA.config.PublicAddress = "auto"
+ netA.config.PublicAddress = "testing"
netA.config.GossipFanout = 1
netB := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netB"})
netB.identityTracker = newMockIdentityTracker(netB.identityTracker)
- netB.config.PublicAddress = "auto"
+ netB.config.PublicAddress = "testing"
netB.config.GossipFanout = 1
netA.Start()
@@ -1380,12 +1380,12 @@ func TestPeeringSenderIdentityChallengeOnly(t *testing.T) {
netA := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netA"})
netA.identityTracker = newMockIdentityTracker(netA.identityTracker)
- netA.config.PublicAddress = "auto"
+ netA.config.PublicAddress = "testing"
netA.config.GossipFanout = 1
netB := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netB"})
netB.identityTracker = newMockIdentityTracker(netB.identityTracker)
- //netB.config.PublicAddress = "auto"
+ //netB.config.PublicAddress = "testing"
netB.config.GossipFanout = 1
netA.Start()
@@ -1445,12 +1445,12 @@ func TestPeeringReceiverIdentityChallengeOnly(t *testing.T) {
netA := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netA"})
netA.identityTracker = newMockIdentityTracker(netA.identityTracker)
- //netA.config.PublicAddress = "auto"
+ //netA.config.PublicAddress = "testing"
netA.config.GossipFanout = 1
netB := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netB"})
netB.identityTracker = newMockIdentityTracker(netB.identityTracker)
- netB.config.PublicAddress = "auto"
+ netB.config.PublicAddress = "testing"
netB.config.GossipFanout = 1
netA.Start()
@@ -1512,7 +1512,7 @@ func TestPeeringIncorrectDeduplicationName(t *testing.T) {
netA := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netA"})
netA.identityTracker = newMockIdentityTracker(netA.identityTracker)
- netA.config.PublicAddress = "auto"
+ netA.config.PublicAddress = "testing"
netA.config.GossipFanout = 1
netB := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netB"})
@@ -1701,7 +1701,7 @@ func TestPeeringWithBadIdentityChallenge(t *testing.T) {
t.Logf("Running Peering with Identity Challenge Test: %s", tc.name)
netA := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netA"})
netA.identityTracker = newMockIdentityTracker(netA.identityTracker)
- netA.config.PublicAddress = "auto"
+ netA.config.PublicAddress = "testing"
netA.config.GossipFanout = 1
scheme := newMockIdentityScheme(t)
@@ -1710,7 +1710,7 @@ func TestPeeringWithBadIdentityChallenge(t *testing.T) {
netB := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netB"})
netB.identityTracker = newMockIdentityTracker(netB.identityTracker)
- netB.config.PublicAddress = "auto"
+ netB.config.PublicAddress = "testing"
netB.config.GossipFanout = 1
netA.Start()
@@ -1844,12 +1844,12 @@ func TestPeeringWithBadIdentityChallengeResponse(t *testing.T) {
t.Logf("Running Peering with Identity Challenge Response Test: %s", tc.name)
netA := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netA"})
netA.identityTracker = newMockIdentityTracker(netA.identityTracker)
- netA.config.PublicAddress = "auto"
+ netA.config.PublicAddress = "testing"
netA.config.GossipFanout = 1
netB := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netB"})
netB.identityTracker = newMockIdentityTracker(netB.identityTracker)
- netB.config.PublicAddress = "auto"
+ netB.config.PublicAddress = "testing"
netB.config.GossipFanout = 1
scheme := newMockIdentityScheme(t)
@@ -1997,7 +1997,7 @@ func TestPeeringWithBadIdentityVerification(t *testing.T) {
t.Logf("Running Peering with Identity Verification Test: %s", tc.name)
netA := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netA"})
netA.identityTracker = newMockIdentityTracker(netA.identityTracker)
- netA.config.PublicAddress = "auto"
+ netA.config.PublicAddress = "testing"
netA.config.GossipFanout = 1
scheme := newMockIdentityScheme(t)
@@ -2006,7 +2006,7 @@ func TestPeeringWithBadIdentityVerification(t *testing.T) {
netB := makeTestWebsocketNode(t, testWebsocketLogNameOption{"netB"})
netB.identityTracker = newMockIdentityTracker(netB.identityTracker)
- netB.config.PublicAddress = "auto"
+ netB.config.PublicAddress = "testing"
netB.config.GossipFanout = 1
// if the key is occupied, make the tracker fail to insert the peer
if tc.occupied {
diff --git a/scripts/algorand_node_log.json b/scripts/algorand_node_log.json
new file mode 100644
index 000000000..78659dc3a
--- /dev/null
+++ b/scripts/algorand_node_log.json
@@ -0,0 +1,58 @@
+{
+ "algorand_node_log" : {
+ "title" : "Algorand Node Log Format",
+ "description" : "Log format for logrus, used by go-algorand.",
+ "url" : "https://github.com/sirupsen/logrus",
+ "level-field" : "level",
+ "timestamp-field" : "time",
+ "json": true,
+ "body-field": "msg",
+ "line-format" : [
+ { "field" : "time", "timestamp-format": "%b %d %H:%M:%S" },
+ " ",
+ { "field" : "level", "text-transform": "uppercase", "min-width": 4, "max-width": 4, "overflow": "truncate"},
+ " ",
+ "[", { "field": "file" }, ":", {"field": "line"}, "]",
+ " ",
+ { "field": "Context" },
+ " ",
+ { "field" : "msg" },
+ " [", { "field": "function" }, "]"
+ ],
+ "hide-extra": true,
+ "level" : {
+ "info" : "info",
+ "error" : "error",
+ "warning" : "warning",
+ "debug" : "debug"
+ },
+ "value": {
+ "file": {
+ "kind": "string",
+ "identifier": true
+ },
+ "line": {
+ "kind": "integer",
+ "foreign-key": true
+ },
+ "function": {
+ "kind": "string",
+ "identifier": true
+ },
+ "Context": {
+ "kind": "string",
+ "identifier": true
+ }
+ },
+ "sample": [
+ {
+ "line": "{\"file\":\"trackerdbV2.go\",\"function\":\"github.com/algorand/go-algorand/ledger/store.(*trackerDBSchemaInitializer).upgradeDatabaseSchema0\",\"level\":\"info\",\"line\":203,\"msg\":\"upgradeDatabaseSchema0 initializing schema\",\"name\":\"\",\"time\":\"2022-12-29T16:26:58.478738+02:00\"}",
+ "level": "info"
+ },
+ {
+ "line": "{\"Context\":\"sync\",\"details\":{\"StartRound\":0},\"file\":\"telemetry.go\",\"function\":\"github.com/algorand/go-algorand/logging.(*telemetryState).logTelemetry\",\"instanceName\":\"iFepr+AcMdoqEg+2\",\"level\":\"info\",\"line\":261,\"msg\":\"/ApplicationState/CatchupStart\",\"name\":\"\",\"session\":\"\",\"time\":\"2022-12-29T16:26:58.763458+02:00\",\"v\":\"3.14.167910\"}",
+ "level": "info"
+ }
+ ]
+ }
+}
diff --git a/scripts/configure_dev.sh b/scripts/configure_dev.sh
index 6eb00df3d..c1190bf5b 100755
--- a/scripts/configure_dev.sh
+++ b/scripts/configure_dev.sh
@@ -85,6 +85,8 @@ elif [ "${OS}" = "darwin" ]; then
install_or_upgrade autoconf
install_or_upgrade automake
install_or_upgrade python3
+ install_or_upgrade lnav
+ lnav -i "$SCRIPTPATH/algorand_node_log.json"
fi
elif [ "${OS}" = "windows" ]; then
if ! $msys2 pacman -S --disable-download-timeout --noconfirm git automake autoconf m4 libtool make mingw-w64-x86_64-gcc mingw-w64-x86_64-boost mingw-w64-x86_64-python mingw-w64-x86_64-jq unzip procps; then
diff --git a/test/scripts/e2e_subs/e2e-app-simulate.sh b/test/scripts/e2e_subs/e2e-app-simulate.sh
new file mode 100755
index 000000000..cae8ebeab
--- /dev/null
+++ b/test/scripts/e2e_subs/e2e-app-simulate.sh
@@ -0,0 +1,139 @@
+#!/bin/bash
+
+date '+app-simple-test start %Y%m%d_%H%M%S'
+
+set -e
+set -x
+set -o pipefail
+set -o nounset
+export SHELLOPTS
+
+WALLET=$1
+
+# Directory of this bash program
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+
+gcmd="goal -w ${WALLET}"
+
+ACCOUNT=$(${gcmd} account list|awk '{ print $3 }')
+
+CONST_TRUE="true"
+CONST_FALSE="false"
+
+##############################################
+# WE FIRST TEST TRANSACTION GROUP SIMULATION #
+##############################################
+
+${gcmd} clerk send -a 10000 -f ${ACCOUNT} -t ${ACCOUNT} -o pay1.tx
+${gcmd} clerk send -a 10000 -f ${ACCOUNT} -t ${ACCOUNT} -o pay2.tx
+
+cat pay1.tx pay2.tx | ${gcmd} clerk group -i - -o grouped.tx
+
+# We first test transaction group simulation WITHOUT signatures
+RES=$(${gcmd} clerk simulate -t grouped.tx)
+
+if [[ $(echo "$RES" | jq '."would-succeed"') != $CONST_FALSE ]]; then
+ date '+app-simulate-test FAIL the simulation transaction group without signatures should not succeed %Y%m%d_%H%M%S'
+ false
+fi
+
+# check the simulation failing reason, first transaction has no signature
+if [[ $(echo "$RES" | jq '."txn-groups"[0]."txn-results"[0]."missing-signature"') != $CONST_TRUE ]]; then
+ date '+app-simulate-test FAIL the simulation transaction group FAIL for first transaction has NO signature %Y%m%d_%H%M%S'
+ false
+fi
+
+# check the simulation failing reason, second transaction has no signature
+if [[ $(echo "$RES" | jq '."txn-groups"[0]."txn-results"[1]."missing-signature"') != $CONST_TRUE ]]; then
+ date '+app-simulate-test FAIL the simulation transaction group FAIL for second transaction has NO signature %Y%m%d_%H%M%S'
+ false
+fi
+
+# We then test transaction group simulation WITH signatures
+${gcmd} clerk split -i grouped.tx -o grouped.tx
+
+${gcmd} clerk sign -i grouped-0.tx -o grouped-0.stx
+${gcmd} clerk sign -i grouped-1.tx -o grouped-1.stx
+
+cat grouped-0.stx grouped-1.stx > grouped.stx
+
+RES=$(${gcmd} clerk simulate -t grouped.stx | jq '."would-succeed"')
+
+if [[ $RES != $CONST_TRUE ]]; then
+ date '+app-simulate-test FAIL should pass to simulate self pay transaction group %Y%m%d_%H%M%S'
+ false
+fi
+
+###############################################
+# WE ALSO TEST OVERSPEND IN TRANSACTION GROUP #
+###############################################
+
+${gcmd} clerk send -a 1000000000000 -f ${ACCOUNT} -t ${ACCOUNT} -o pay1.tx
+${gcmd} clerk send -a 10000 -f ${ACCOUNT} -t ${ACCOUNT} -o pay2.tx
+
+cat pay1.tx pay2.tx | ${gcmd} clerk group -i - -o grouped.tx
+
+${gcmd} clerk split -i grouped.tx -o grouped.tx
+
+${gcmd} clerk sign -i grouped-0.tx -o grouped-0.stx
+${gcmd} clerk sign -i grouped-1.tx -o grouped-1.stx
+
+cat grouped-0.stx grouped-1.stx > grouped.stx
+
+RES=$(${gcmd} clerk simulate -t grouped.stx)
+
+if [[ $(echo "$RES" | jq '."would-succeed"') != $CONST_FALSE ]]; then
+ data '+app-simulate-test FAIL should FAIL for overspending in simulate self pay transaction group %Y%m%d_%H%M%S'
+ false
+fi
+
+OVERSPEND_INFO="overspend"
+
+if [[ $(echo "$RES" | jq '."txn-groups"[0]."failure-message"') != *"$OVERSPEND_INFO"* ]]; then
+ data '+app-simulate-test FAIL first overspending transaction in transaction group should contain message OVERSPEND %Y%m%d_%H%M%S'
+ false
+fi
+
+#######################################################
+# NOW WE TRY TO TEST SIMULATION WITH ABI METHOD CALLS #
+#######################################################
+
+printf '#pragma version 2\nint 1' > "${TEMPDIR}/simple-v2.teal"
+
+# Real Create
+RES=$(${gcmd} app method --method "create(uint64)uint64" --arg "1234" --create --approval-prog ${DIR}/tealprogs/app-abi-method-example.teal --clear-prog ${TEMPDIR}/simple-v2.teal --global-byteslices 0 --global-ints 0 --local-byteslices 1 --local-ints 0 --extra-pages 0 --from $ACCOUNT 2>&1 || true)
+EXPECTED="method create(uint64)uint64 succeeded with output: 2468"
+if [[ $RES != *"${EXPECTED}"* ]]; then
+ date '+app-simulate-test FAIL the method call to create(uint64)uint64 should not fail %Y%m%d_%H%M%S'
+ false
+fi
+
+APPID=$(echo "$RES" | grep Created | awk '{ print $6 }')
+
+# SIMULATION! empty()void
+${gcmd} app method --method "empty()void" --app-id $APPID --from $ACCOUNT 2>&1 -o empty.tx
+
+# SIMULATE without a signature first
+RES=$(${gcmd} clerk simulate -t empty.tx)
+
+# confirm that without signature, the simulation should fail
+if [[ $(echo "$RES" | jq '."would-succeed"') != $CONST_FALSE ]]; then
+ date '+app-simulate-test FAIL the simulation call to empty()void without signature should not succeed %Y%m%d_%H%M%S'
+ false
+fi
+
+# check again the simulation failing reason
+if [[ $(echo "$RES" | jq '."txn-groups"[0]."txn-results"[0]."missing-signature"') != $CONST_TRUE ]]; then
+ date '+app-simulate-test FAIL the simulation call to empty()void without signature should fail with missing-signature %Y%m%d_%H%M%S'
+ false
+fi
+
+# SIMULATE with a signature
+${gcmd} clerk sign -i empty.tx -o empty.stx
+RES=$(${gcmd} clerk simulate -t empty.stx | jq '."would-succeed"')
+
+# with signature, simulation app-call should succeed
+if [[ $RES != $CONST_TRUE ]]; then
+ date '+app-simulate-test FAIL the simulation call to empty()void should succeed %Y%m%d_%H%M%S'
+ false
+fi
diff --git a/test/testdata/deployednettemplates/recipes/custom/configs/nonPartNode.json b/test/testdata/deployednettemplates/recipes/custom/configs/nonPartNode.json
index 5b0a52d9d..42d491b2e 100644
--- a/test/testdata/deployednettemplates/recipes/custom/configs/nonPartNode.json
+++ b/test/testdata/deployednettemplates/recipes/custom/configs/nonPartNode.json
@@ -1,5 +1,5 @@
{
"APIEndpoint": "{{APIEndpoint}}",
"APIToken": "{{APIToken}}",
- "ConfigJSONOverride": "{ \"TxPoolExponentialIncreaseFactor\": 1, \"DNSBootstrapID\": \"<network>.algodev.network\", \"DeadlockDetection\": -1, \"BaseLoggerDebugLevel\": 4, \"CadaverSizeTarget\": 0 }"
+ "ConfigJSONOverride": "{ \"TxPoolExponentialIncreaseFactor\": 1, \"DNSBootstrapID\": \"<network>.algodev.network\", \"DeadlockDetection\": -1, \"BaseLoggerDebugLevel\": 4, \"EnableRuntimeMetrics\": true, \"CadaverSizeTarget\": 0 }"
}
diff --git a/test/testdata/deployednettemplates/recipes/custom/configs/relay.json b/test/testdata/deployednettemplates/recipes/custom/configs/relay.json
index 25bb6b5a2..2f621f1a2 100644
--- a/test/testdata/deployednettemplates/recipes/custom/configs/relay.json
+++ b/test/testdata/deployednettemplates/recipes/custom/configs/relay.json
@@ -7,5 +7,5 @@
"TelemetryURI": "{{TelemetryURI}}",
"EnableMetrics": true,
"MetricsURI": "{{MetricsURI}}",
- "ConfigJSONOverride": "{ \"TxPoolExponentialIncreaseFactor\": 1, \"DNSBootstrapID\": \"<network>.algodev.network\", \"DeadlockDetection\": -1, \"EnableIncomingMessageFilter\": true, \"CadaverSizeTarget\": 0, \"PeerPingPeriodSeconds\": 30, \"EnableAgreementReporting\": true, \"EnableAgreementTimeMetrics\": true, \"EnableAssembleStats\": true, \"EnableProcessBlockStats\": true, \"BaseLoggerDebugLevel\": 4, \"EnableProfiler\": true }"
+ "ConfigJSONOverride": "{ \"TxPoolExponentialIncreaseFactor\": 1, \"DNSBootstrapID\": \"<network>.algodev.network\", \"DeadlockDetection\": -1, \"EnableIncomingMessageFilter\": true, \"EnableRuntimeMetrics\": true, \"CadaverSizeTarget\": 0, \"PeerPingPeriodSeconds\": 30, \"EnableAgreementReporting\": true, \"EnableAgreementTimeMetrics\": true, \"EnableAssembleStats\": true, \"EnableProcessBlockStats\": true, \"BaseLoggerDebugLevel\": 4, \"EnableProfiler\": true }"
}
diff --git a/tools/debug/chopper/main.go b/tools/debug/chopper/main.go
new file mode 100644
index 000000000..5e70292f0
--- /dev/null
+++ b/tools/debug/chopper/main.go
@@ -0,0 +1,230 @@
+// Copyright (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/>.
+
+// chopper compares raw Algorand logs for matching catchpoint (balance trie) roots and labels
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/fatih/color"
+ "golang.org/x/text/cases"
+ "golang.org/x/text/language"
+
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/logging/telemetryspec"
+)
+
+const (
+ red = color.FgRed
+ green = color.FgGreen
+ yellow = color.FgYellow
+)
+
+var help = flag.Bool("help", false, "Show help")
+var helpShort = flag.Bool("h", false, "Show help")
+var labels = flag.Bool("labels", false, "Compare catchpoint labels in addition to roots")
+var labelsShort = flag.Bool("l", false, "Compare catchpoint labels in addition to roots")
+
+func usage() {
+ fmt.Fprintln(os.Stderr, `Utility to extract and compare balance root and catchpoint labels messages from algod log files (node.log)
+Usage: ./chopper [--labels] file1 file2`)
+}
+
+// logEntry is json representing catchpoint root message telemetry
+type logEntry struct {
+ Details telemetryspec.CatchpointRootUpdateEventDetails
+}
+
+// rootLabelInfo is parsed roots/labels from a log file
+type rootLabelInfo struct {
+ roots map[basics.Round]*telemetryspec.CatchpointRootUpdateEventDetails
+ labels map[basics.Round]string
+}
+
+// extractEntries reads the log file line by line and collects root and labels entries
+func extractEntries(filename string, checkLabels bool) rootLabelInfo {
+ f, err := os.Open(filename)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error opening %s: %s\n", filename, err.Error())
+ os.Exit(1)
+ }
+ defer f.Close()
+
+ s := bufio.NewScanner(f)
+
+ var re *regexp.Regexp
+ if checkLabels {
+ re = regexp.MustCompile(`Creating a catchpoint label (\d+#[A-Z0-9]+)\s+for round=(\d+).*`)
+ }
+
+ result := rootLabelInfo{
+ roots: make(map[basics.Round]*telemetryspec.CatchpointRootUpdateEventDetails),
+ }
+ if checkLabels {
+ result.labels = make(map[basics.Round]string)
+ }
+
+ for s.Scan() {
+ line := s.Text()
+ if line[0] == '{' && strings.Contains(line[:20], "Root") {
+ var entry logEntry
+ if err := json.Unmarshal([]byte(line), &entry); err != nil {
+ fmt.Fprintf(os.Stderr, "Error reading catchpoint root entry %s: %s\n", filename, err.Error())
+ continue
+ }
+ result.roots[basics.Round(entry.Details.NewBase)] = &entry.Details
+ } else if checkLabels && strings.HasPrefix(line, `{"file":"catchpointlabel.go"`) {
+ entry := map[string]interface{}{}
+ if err := json.Unmarshal([]byte(line), &entry); err != nil {
+ fmt.Fprintf(os.Stderr, "Error reading catchpoint label entry %s: %s\n", filename, err.Error())
+ continue
+ }
+ matches := re.FindStringSubmatch(entry["msg"].(string))
+ if len(matches) != 3 {
+ fmt.Fprintf(os.Stderr, "No catchpoint label match %s: %s %s\n", filename, matches, entry["msg"])
+ continue
+ }
+ uintRound, err := strconv.ParseUint(matches[2], 10, 64)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Cannot parse round %s: %s\n", filename, matches[1])
+ continue
+ }
+ result.labels[basics.Round(uintRound)] = matches[1]
+ }
+ }
+
+ if err := s.Err(); err != nil {
+ fmt.Fprintf(os.Stderr, "Error reading lines from %s: %s\n", filename, err.Error())
+ os.Exit(1)
+ }
+
+ return result
+}
+
+type reportData struct {
+ what string
+ size1 int
+ size2 int
+ matched int
+ mismatched []interface{}
+ errReporter func(interface{})
+}
+
+// report prints out stats about matched and mismatched roots or labels
+func report(rd reportData) {
+ fmt.Printf("%s in first: %d, second: %d\n", cases.Title(language.English).String(rd.what), rd.size1, rd.size2)
+
+ const matchedStr = "Matched %s: %d"
+ c := yellow
+ if rd.matched > 0 {
+ c = green
+ }
+ fmt.Println(color.New(c).Sprintf(matchedStr, rd.what, rd.matched))
+
+ const mismatchedStr = "Mismatched %s: %d"
+ c = green
+ if len(rd.mismatched) > 0 {
+ c = red
+ }
+ fmt.Println(color.New(c).Sprintf(mismatchedStr, rd.what, len(rd.mismatched)))
+ if len(rd.mismatched) > 0 {
+ for _, entry := range rd.mismatched {
+ rd.errReporter(entry)
+ }
+ }
+ fmt.Printf("Other %s in first: %d, second: %d\n", rd.what, rd.size1-rd.matched-len(rd.mismatched), rd.size2-rd.matched-len(rd.mismatched))
+}
+
+func main() {
+ flag.Parse()
+
+ if *help || *helpShort || len(flag.Args()) < 2 {
+ usage()
+ os.Exit(1)
+ }
+
+ checkLabels := *labels || *labelsShort
+
+ file1 := flag.Args()[0]
+ file2 := flag.Args()[1]
+
+ // load data
+ info1 := extractEntries(file1, checkLabels)
+ info2 := extractEntries(file2, checkLabels)
+
+ // match roots
+ matchedRoots := 0
+ var mismatchedRoots []interface{}
+ for rnd, tree1 := range info1.roots {
+ if tree2, ok := info2.roots[rnd]; ok {
+ if tree1.Root == tree2.Root {
+ matchedRoots++
+ } else {
+ mismatchedRoots = append(mismatchedRoots, [2]*telemetryspec.CatchpointRootUpdateEventDetails{tree1, tree2})
+ }
+ }
+ }
+
+ // match labels
+ matchedLabels := 0
+ var mismatchedLabels []interface{}
+ if checkLabels {
+ for rnd, label1 := range info1.labels {
+ if label2, ok := info2.labels[rnd]; ok {
+ if label1 == label2 {
+ matchedLabels++
+ } else {
+ mismatchedLabels = append(mismatchedLabels, [2]string{label1, label2})
+ }
+ }
+ }
+
+ }
+
+ report(reportData{
+ what: "roots",
+ size1: len(info1.roots),
+ size2: len(info2.roots),
+ matched: matchedRoots,
+ mismatched: mismatchedRoots,
+ errReporter: func(e interface{}) {
+ entry := e.([2]*telemetryspec.CatchpointRootUpdateEventDetails)
+ fmt.Printf("NewBase: %d, first: (%d, %s), second (%d,%s)\n", entry[0].NewBase, entry[0].OldBase, entry[0].Root, entry[1].OldBase, entry[1].Root)
+ },
+ })
+
+ if checkLabels {
+ report(reportData{
+ what: "labels",
+ size1: len(info1.labels),
+ size2: len(info2.labels),
+ matched: matchedLabels,
+ mismatched: mismatchedLabels,
+ errReporter: func(e interface{}) {
+ entry := e.([2]string)
+ fmt.Printf("first: %s != %s second\n", entry[0], entry[1])
+ },
+ })
+ }
+}
diff --git a/tools/debug/jslog b/tools/debug/jslog
index 540ebb66c..fe27719f0 100755
--- a/tools/debug/jslog
+++ b/tools/debug/jslog
@@ -96,7 +96,7 @@ class LogFile:
return None
if when:
- when = datetime.datetime.strptime(when, '%Y-%m-%dT%H:%M:%S%z')
+ when = datetime.datetime.strptime(when, '%Y-%m-%dT%H:%M:%S.%f%z')
now = time.time()
dt = when.timestamp() - now
# TODO: format sub-second if available
diff --git a/util/s3/s3Helper.go b/util/s3/s3Helper.go
index 8c70f14ed..9fc6f0691 100644
--- a/util/s3/s3Helper.go
+++ b/util/s3/s3Helper.go
@@ -245,15 +245,15 @@ func GetVersionFromName(name string) (version uint64, err error) {
return
}
var val uint64
- for index, match := range submatchAll[0] {
- if index > 0 {
- version <<= 16
- val, err = strconv.ParseUint(match, 10, 0)
- if err != nil {
- return
- }
- version += val
+ submatch := submatchAll[0][1:] // skip the first match which is the whole string
+ offsets := []int{0, 16, 24} // some bits for major (not really restricted), 16 bits for minor, 24 bits for patch
+ for index, match := range submatch {
+ version <<= offsets[index]
+ val, err = strconv.ParseUint(match, 10, 0)
+ if err != nil {
+ return
}
+ version += val
}
return
}
@@ -262,13 +262,13 @@ func GetVersionFromName(name string) (version uint64, err error) {
func GetVersionPartsFromVersion(version uint64) (major uint64, minor uint64, patch uint64, err error) {
val := version
- if val < 1<<32 {
+ if val < 1<<40 {
err = errors.New("versions below 1.0.0 not supported")
return
}
- patch = val & 0xffff
- val >>= 16
+ patch = val & 0xffffff
+ val >>= 24
minor = val & 0xffff
val >>= 16
major = val
diff --git a/util/s3/s3Helper_test.go b/util/s3/s3Helper_test.go
index c0502a3b0..89cf09188 100644
--- a/util/s3/s3Helper_test.go
+++ b/util/s3/s3Helper_test.go
@@ -17,6 +17,7 @@
package s3
import (
+ "fmt"
"os"
"reflect"
"testing"
@@ -174,6 +175,7 @@ func TestMakeS3SessionForDownloadWithBucket(t *testing.T) {
func TestGetVersionFromName(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
type args struct {
name string
@@ -181,12 +183,12 @@ func TestGetVersionFromName(t *testing.T) {
expected uint64
}
tests := []args{
- {name: "test 1 (major)", version: "_1.0.0", expected: 1 * 1 << 32},
- {name: "test 2 (major)", version: "_2.0.0", expected: 2 * 1 << 32},
- {name: "test 3 (minor)", version: "_1.1.0", expected: 1*1<<32 + 1*1<<16},
- {name: "test 4 (minor)", version: "_1.2.0", expected: 1*1<<32 + 2*1<<16},
- {name: "test 5 (patch)", version: "_1.0.1", expected: 1*1<<32 + 1},
- {name: "test 6 (patch)", version: "_1.0.2", expected: 1*1<<32 + 2},
+ {name: "test 1 (major)", version: "_1.0.0", expected: 1 * 1 << 40},
+ {name: "test 2 (major)", version: "_2.0.0", expected: 2 * 1 << 40},
+ {name: "test 3 (minor)", version: "_1.1.0", expected: 1*1<<40 + 1*1<<24},
+ {name: "test 4 (minor)", version: "_1.2.0", expected: 1*1<<40 + 2*1<<24},
+ {name: "test 5 (patch)", version: "_1.0.1", expected: 1*1<<40 + 1},
+ {name: "test 6 (patch)", version: "_1.0.2", expected: 1*1<<40 + 2},
}
for _, test := range tests {
@@ -196,8 +198,24 @@ func TestGetVersionFromName(t *testing.T) {
}
}
+func TestGetVersionFromNameCompare(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ name1 := "config_3.13.170018.tar.gz"
+ name2 := "config_3.15.157.tar.gz"
+
+ ver1, err := GetVersionFromName(name1)
+ require.NoError(t, err)
+ ver2, err := GetVersionFromName(name2)
+ require.NoError(t, err)
+
+ require.Less(t, ver1, ver2)
+}
+
func TestGetPartsFromVersion(t *testing.T) {
partitiontest.PartitionTest(t)
+ t.Parallel()
type args struct {
name string
@@ -207,12 +225,12 @@ func TestGetPartsFromVersion(t *testing.T) {
expPatch uint64
}
tests := []args{
- {name: "test 1 (major)", version: 1 * 1 << 32, expMajor: 1, expMinor: 0, expPatch: 0},
- {name: "test 2 (major)", version: 2 * 1 << 32, expMajor: 2, expMinor: 0, expPatch: 0},
- {name: "test 3 (minor)", version: 1*1<<32 + 1*1<<16, expMajor: 1, expMinor: 1, expPatch: 0},
- {name: "test 4 (minor)", version: 1*1<<32 + 2*1<<16, expMajor: 1, expMinor: 2, expPatch: 0},
- {name: "test 5 (patch)", version: 1*1<<32 + 1, expMajor: 1, expMinor: 0, expPatch: 1},
- {name: "test 6 (patch)", version: 1*1<<32 + 2, expMajor: 1, expMinor: 0, expPatch: 2},
+ {name: "test 1 (major)", version: 1 * 1 << 40, expMajor: 1, expMinor: 0, expPatch: 0},
+ {name: "test 2 (major)", version: 2 * 1 << 40, expMajor: 2, expMinor: 0, expPatch: 0},
+ {name: "test 3 (minor)", version: 1*1<<40 + 1*1<<24, expMajor: 1, expMinor: 1, expPatch: 0},
+ {name: "test 4 (minor)", version: 1*1<<40 + 2*1<<24, expMajor: 1, expMinor: 2, expPatch: 0},
+ {name: "test 5 (patch)", version: 1*1<<40 + 1, expMajor: 1, expMinor: 0, expPatch: 1},
+ {name: "test 6 (patch)", version: 1*1<<40 + 2, expMajor: 1, expMinor: 0, expPatch: 2},
}
for _, test := range tests {
@@ -223,6 +241,35 @@ func TestGetPartsFromVersion(t *testing.T) {
require.Equal(t, test.expPatch, actualPatch, test.name)
}
- _, _, _, err := GetVersionPartsFromVersion(1<<32 - 1)
+ _, _, _, err := GetVersionPartsFromVersion(1<<40 - 1)
require.Error(t, err, "Versions less than 1.0.0 should not be parsed.")
}
+
+func TestGetPartsFromVersionEndToEnd(t *testing.T) {
+ partitiontest.PartitionTest(t)
+ t.Parallel()
+
+ type args struct {
+ major uint64
+ minor uint64
+ patch uint64
+ }
+ tests := []args{
+ {major: 1, minor: 0, patch: 0},
+ {major: 3, minor: 13, patch: 170018},
+ {major: 3, minor: 15, patch: 157},
+ }
+
+ for _, test := range tests {
+ name := fmt.Sprintf("config_%d.%d.%d.tar.gz", test.major, test.minor, test.patch)
+ t.Run(name, func(t *testing.T) {
+ ver, err := GetVersionFromName(name)
+ require.NoError(t, err)
+ actualMajor, actualMinor, actualPatch, err := GetVersionPartsFromVersion(ver)
+ require.NoError(t, err)
+ require.Equal(t, test.major, actualMajor)
+ require.Equal(t, test.minor, actualMinor)
+ require.Equal(t, test.patch, actualPatch)
+ })
+ }
+}