From cc8dbb7f768adc659919c50b91d9b88bb519918c Mon Sep 17 00:00:00 2001 From: Gary Malouf <982483+gmalouf@users.noreply.github.com> Date: Mon, 11 Nov 2024 11:59:26 -0500 Subject: [PATCH 1/6] Perf Tweaks (#6167) Set MaxConnectionsPerIP to 4, ERL should kick in when it hits the `backlogCongestionThreshold`. --- config/localTemplate.go | 6 +- config/local_defaults.go | 4 +- data/txHandler.go | 6 +- installer/config.json.example | 4 +- test/testdata/configs/config-v34.json | 1 - test/testdata/configs/config-v35.json | 145 ++++++++++++++++++ .../nettemplates/RichAccountStateProof.json | 12 +- util/rateLimit.go | 20 +-- util/rateLimit_test.go | 26 ++-- 9 files changed, 186 insertions(+), 38 deletions(-) create mode 100644 test/testdata/configs/config-v35.json diff --git a/config/localTemplate.go b/config/localTemplate.go index bc9df5fbbc..413d48a05a 100644 --- a/config/localTemplate.go +++ b/config/localTemplate.go @@ -44,7 +44,7 @@ type Local struct { // Version tracks the current version of the defaults so we can migrate old -> new // This is specifically important whenever we decide to change the default value // for an existing parameter. This field tag must be updated any time we add a new version. - Version uint32 `version[0]:"0" version[1]:"1" version[2]:"2" version[3]:"3" version[4]:"4" version[5]:"5" version[6]:"6" version[7]:"7" version[8]:"8" version[9]:"9" version[10]:"10" version[11]:"11" version[12]:"12" version[13]:"13" version[14]:"14" version[15]:"15" version[16]:"16" version[17]:"17" version[18]:"18" version[19]:"19" version[20]:"20" version[21]:"21" version[22]:"22" version[23]:"23" version[24]:"24" version[25]:"25" version[26]:"26" version[27]:"27" version[28]:"28" version[29]:"29" version[30]:"30" version[31]:"31" version[32]:"32" version[33]:"33" version[34]:"34"` + Version uint32 `version[0]:"0" version[1]:"1" version[2]:"2" version[3]:"3" version[4]:"4" version[5]:"5" version[6]:"6" version[7]:"7" version[8]:"8" version[9]:"9" version[10]:"10" version[11]:"11" version[12]:"12" version[13]:"13" version[14]:"14" version[15]:"15" version[16]:"16" version[17]:"17" version[18]:"18" version[19]:"19" version[20]:"20" version[21]:"21" version[22]:"22" version[23]:"23" version[24]:"24" version[25]:"25" version[26]:"26" version[27]:"27" version[28]:"28" version[29]:"29" version[30]:"30" version[31]:"31" version[32]:"32" version[33]:"33" version[34]:"34" version[35]:"35"` // Archival nodes retain a full copy of the block history. Non-Archival nodes will delete old blocks and only retain what's need to properly validate blockchain messages (the precise number of recent blocks depends on the consensus parameters. Currently the last 1321 blocks are required). This means that non-Archival nodes require significantly less storage than Archival nodes. If setting this to true for the first time, the existing ledger may need to be deleted to get the historical values stored as the setting only affects current blocks forward. To do this, shutdown the node and delete all .sqlite files within the data/testnet-version directory, except the crash.sqlite file. Restart the node and wait for the node to sync. Archival bool `version[0]:"false"` @@ -66,7 +66,7 @@ type Local struct { PublicAddress string `version[0]:""` // MaxConnectionsPerIP is the maximum number of connections allowed per IP address. - MaxConnectionsPerIP int `version[3]:"30" version[27]:"15"` + MaxConnectionsPerIP int `version[3]:"30" version[27]:"15" version[35]:"4"` // PeerPingPeriodSeconds is deprecated and unused. PeerPingPeriodSeconds int `version[0]:"0"` @@ -171,7 +171,7 @@ type Local struct { EndpointAddress string `version[0]:"127.0.0.1:0"` // Respond to Private Network Access preflight requests sent to the node. Useful when a public website is trying to access a node that's hosted on a local network. - EnablePrivateNetworkAccessHeader bool `version[34]:"false"` + EnablePrivateNetworkAccessHeader bool `version[35]:"false"` // RestReadTimeoutSeconds is passed to the API servers rest http.Server implementation. RestReadTimeoutSeconds int `version[4]:"15"` diff --git a/config/local_defaults.go b/config/local_defaults.go index 4d2abb0053..7464853bb4 100644 --- a/config/local_defaults.go +++ b/config/local_defaults.go @@ -20,7 +20,7 @@ package config var defaultLocal = Local{ - Version: 34, + Version: 35, AccountUpdatesStatsInterval: 5000000000, AccountsRebuildSynchronousMode: 1, AgreementIncomingBundlesQueueLength: 15, @@ -111,7 +111,7 @@ var defaultLocal = Local{ MaxAcctLookback: 4, MaxBlockHistoryLookback: 0, MaxCatchpointDownloadDuration: 43200000000000, - MaxConnectionsPerIP: 15, + MaxConnectionsPerIP: 4, MinCatchpointFileDownloadBytesPerSecond: 20480, NetAddress: "", NetworkMessageTraceServer: "", diff --git a/data/txHandler.go b/data/txHandler.go index ec3a84cc1f..3b1cbe5e08 100644 --- a/data/txHandler.go +++ b/data/txHandler.go @@ -622,6 +622,7 @@ func (handler *TxHandler) incomingMsgDupCheck(data []byte) (*crypto.Digest, bool // - a boolean indicating if the sender is rate limited func (handler *TxHandler) incomingMsgErlCheck(sender network.DisconnectablePeer) (*util.ErlCapacityGuard, bool) { var capguard *util.ErlCapacityGuard + var isCMEnabled bool var err error if handler.erl != nil { congestedERL := float64(cap(handler.backlogQueue))*handler.backlogCongestionThreshold < float64(len(handler.backlogQueue)) @@ -629,8 +630,9 @@ func (handler *TxHandler) incomingMsgErlCheck(sender network.DisconnectablePeer) // if the elastic rate limiter cannot vend a capacity, the error it returns // is sufficient to indicate that we should enable Congestion Control, because // an issue in vending capacity indicates the underlying resource (TXBacklog) is full - capguard, err = handler.erl.ConsumeCapacity(sender.(util.ErlClient)) - if err != nil { + capguard, isCMEnabled, err = handler.erl.ConsumeCapacity(sender.(util.ErlClient)) + if err != nil || // did ERL ask to enable congestion control? + (!isCMEnabled && congestedERL) { // is CM not currently enabled, but queue is congested? handler.erl.EnableCongestionControl() // if there is no capacity, it is the same as if we failed to put the item onto the backlog, so report such transactionMessagesDroppedFromBacklog.Inc(nil) diff --git a/installer/config.json.example b/installer/config.json.example index 8e93d95e28..9d3146b6cb 100644 --- a/installer/config.json.example +++ b/installer/config.json.example @@ -1,5 +1,5 @@ { - "Version": 34, + "Version": 35, "AccountUpdatesStatsInterval": 5000000000, "AccountsRebuildSynchronousMode": 1, "AgreementIncomingBundlesQueueLength": 15, @@ -90,7 +90,7 @@ "MaxAcctLookback": 4, "MaxBlockHistoryLookback": 0, "MaxCatchpointDownloadDuration": 43200000000000, - "MaxConnectionsPerIP": 15, + "MaxConnectionsPerIP": 4, "MinCatchpointFileDownloadBytesPerSecond": 20480, "NetAddress": "", "NetworkMessageTraceServer": "", diff --git a/test/testdata/configs/config-v34.json b/test/testdata/configs/config-v34.json index 8e93d95e28..db4420ed9d 100644 --- a/test/testdata/configs/config-v34.json +++ b/test/testdata/configs/config-v34.json @@ -57,7 +57,6 @@ "EnableP2P": false, "EnableP2PHybridMode": false, "EnablePingHandler": true, - "EnablePrivateNetworkAccessHeader": false, "EnableProcessBlockStats": false, "EnableProfiler": false, "EnableRequestLogger": false, diff --git a/test/testdata/configs/config-v35.json b/test/testdata/configs/config-v35.json new file mode 100644 index 0000000000..9d3146b6cb --- /dev/null +++ b/test/testdata/configs/config-v35.json @@ -0,0 +1,145 @@ +{ + "Version": 35, + "AccountUpdatesStatsInterval": 5000000000, + "AccountsRebuildSynchronousMode": 1, + "AgreementIncomingBundlesQueueLength": 15, + "AgreementIncomingProposalsQueueLength": 50, + "AgreementIncomingVotesQueueLength": 20000, + "AnnounceParticipationKey": true, + "Archival": false, + "BaseLoggerDebugLevel": 4, + "BlockDBDir": "", + "BlockServiceCustomFallbackEndpoints": "", + "BlockServiceMemCap": 500000000, + "BroadcastConnectionsLimit": -1, + "CadaverDirectory": "", + "CadaverSizeTarget": 0, + "CatchpointDir": "", + "CatchpointFileHistoryLength": 365, + "CatchpointInterval": 10000, + "CatchpointTracking": 0, + "CatchupBlockDownloadRetryAttempts": 1000, + "CatchupBlockValidateMode": 0, + "CatchupFailurePeerRefreshRate": 10, + "CatchupGossipBlockFetchTimeoutSec": 4, + "CatchupHTTPBlockFetchTimeoutSec": 4, + "CatchupLedgerDownloadRetryAttempts": 50, + "CatchupParallelBlocks": 16, + "ColdDataDir": "", + "ConnectionsRateLimitingCount": 60, + "ConnectionsRateLimitingWindowSeconds": 1, + "CrashDBDir": "", + "DNSBootstrapID": ".algorand.network?backup=.algorand.net&dedup=.algorand-.(network|net)", + "DNSSecurityFlags": 9, + "DeadlockDetection": 0, + "DeadlockDetectionThreshold": 30, + "DisableAPIAuth": false, + "DisableLedgerLRUCache": false, + "DisableLocalhostConnectionRateLimit": true, + "DisableNetworking": false, + "DisableOutgoingConnectionThrottling": false, + "EnableAccountUpdatesStats": false, + "EnableAgreementReporting": false, + "EnableAgreementTimeMetrics": false, + "EnableAssembleStats": false, + "EnableBlockService": false, + "EnableDHTProviders": false, + "EnableDeveloperAPI": false, + "EnableExperimentalAPI": false, + "EnableFollowMode": false, + "EnableGossipBlockService": true, + "EnableGossipService": true, + "EnableIncomingMessageFilter": false, + "EnableLedgerService": false, + "EnableMetricReporting": false, + "EnableNetDevMetrics": false, + "EnableOutgoingNetworkMessageFiltering": true, + "EnableP2P": false, + "EnableP2PHybridMode": false, + "EnablePingHandler": true, + "EnablePrivateNetworkAccessHeader": false, + "EnableProcessBlockStats": false, + "EnableProfiler": false, + "EnableRequestLogger": false, + "EnableRuntimeMetrics": false, + "EnableTopAccountsReporting": false, + "EnableTxBacklogAppRateLimiting": true, + "EnableTxBacklogRateLimiting": true, + "EnableTxnEvalTracer": false, + "EnableUsageLog": false, + "EnableVerbosedTransactionSyncLogging": false, + "EndpointAddress": "127.0.0.1:0", + "FallbackDNSResolverAddress": "", + "ForceFetchTransactions": false, + "ForceRelayMessages": false, + "GoMemLimit": 0, + "GossipFanout": 4, + "HeartbeatUpdateInterval": 600, + "HotDataDir": "", + "IncomingConnectionsLimit": 2400, + "IncomingMessageFilterBucketCount": 5, + "IncomingMessageFilterBucketSize": 512, + "LedgerSynchronousMode": 2, + "LogArchiveDir": "", + "LogArchiveMaxAge": "", + "LogArchiveName": "node.archive.log", + "LogFileDir": "", + "LogSizeLimit": 1073741824, + "MaxAPIBoxPerApplication": 100000, + "MaxAPIResourcesPerAccount": 100000, + "MaxAcctLookback": 4, + "MaxBlockHistoryLookback": 0, + "MaxCatchpointDownloadDuration": 43200000000000, + "MaxConnectionsPerIP": 4, + "MinCatchpointFileDownloadBytesPerSecond": 20480, + "NetAddress": "", + "NetworkMessageTraceServer": "", + "NetworkProtocolVersion": "", + "NodeExporterListenAddress": ":9100", + "NodeExporterPath": "./node_exporter", + "OptimizeAccountsDatabaseOnStartup": false, + "OutgoingMessageFilterBucketCount": 3, + "OutgoingMessageFilterBucketSize": 128, + "P2PHybridIncomingConnectionsLimit": 1200, + "P2PHybridNetAddress": "", + "P2PPersistPeerID": false, + "P2PPrivateKeyLocation": "", + "ParticipationKeysRefreshInterval": 60000000000, + "PeerConnectionsUpdateInterval": 3600, + "PeerPingPeriodSeconds": 0, + "PriorityPeers": {}, + "ProposalAssemblyTime": 500000000, + "PublicAddress": "", + "ReconnectTime": 60000000000, + "ReservedFDs": 256, + "RestConnectionsHardLimit": 2048, + "RestConnectionsSoftLimit": 1024, + "RestReadTimeoutSeconds": 15, + "RestWriteTimeoutSeconds": 120, + "RunHosted": false, + "StateproofDir": "", + "StorageEngine": "sqlite", + "SuggestedFeeBlockHistory": 3, + "SuggestedFeeSlidingWindowSize": 50, + "TLSCertFile": "", + "TLSKeyFile": "", + "TelemetryToLog": true, + "TrackerDBDir": "", + "TransactionSyncDataExchangeRate": 0, + "TransactionSyncSignificantMessageThreshold": 0, + "TxBacklogAppTxPerSecondRate": 100, + "TxBacklogAppTxRateLimiterMaxSize": 1048576, + "TxBacklogRateLimitingCongestionPct": 50, + "TxBacklogReservedCapacityPerPeer": 20, + "TxBacklogServiceRateWindowSeconds": 10, + "TxBacklogSize": 26000, + "TxIncomingFilterMaxSize": 500000, + "TxIncomingFilteringFlags": 1, + "TxPoolExponentialIncreaseFactor": 2, + "TxPoolSize": 75000, + "TxSyncIntervalSeconds": 60, + "TxSyncServeResponseSize": 1000000, + "TxSyncTimeoutSeconds": 30, + "UseXForwardedForAddressField": "", + "VerifiedTranscationsCacheSize": 150000 +} diff --git a/test/testdata/nettemplates/RichAccountStateProof.json b/test/testdata/nettemplates/RichAccountStateProof.json index 578fb0e3c5..341bb04d0b 100644 --- a/test/testdata/nettemplates/RichAccountStateProof.json +++ b/test/testdata/nettemplates/RichAccountStateProof.json @@ -14,18 +14,20 @@ "Nodes": [ { "Name": "Relay0", + "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}", "IsRelay": true, "Wallets": [] }, { "Name": "Relay1", + "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}", "IsRelay": true, "Wallets": [] }, - { "Name": "richNode", "Wallets": [ { "Name": "richWallet", "ParticipationOnly": false } ] }, - { "Name": "Node1", "Wallets": [ { "Name": "Wallet1", "ParticipationOnly": false } ] }, - { "Name": "Node2", "Wallets": [ { "Name": "Wallet2", "ParticipationOnly": false } ] }, - { "Name": "Node3", "Wallets": [ { "Name": "Wallet3", "ParticipationOnly": false } ] }, - { "Name": "poorNode", "Wallets": [ { "Name": "poorWallet", "ParticipationOnly": false } ] } + { "Name": "richNode", "Wallets": [ { "Name": "richWallet", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] }, + { "Name": "Node1", "Wallets": [ { "Name": "Wallet1", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] }, + { "Name": "Node2", "Wallets": [ { "Name": "Wallet2", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] }, + { "Name": "Node3", "Wallets": [ { "Name": "Wallet3", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] }, + { "Name": "poorNode", "Wallets": [ { "Name": "poorWallet", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] } ] } diff --git a/util/rateLimit.go b/util/rateLimit.go index 8db9763474..3fea7f8912 100644 --- a/util/rateLimit.go +++ b/util/rateLimit.go @@ -170,16 +170,16 @@ func (erl *ElasticRateLimiter) DisableCongestionControl() { // Returns an error if the capacity could not be vended, which could be: // - there is not sufficient free capacity to assign a reserved capacity block // - there is no reserved or shared capacity available for the client -func (erl *ElasticRateLimiter) ConsumeCapacity(c ErlClient) (*ErlCapacityGuard, error) { +func (erl *ElasticRateLimiter) ConsumeCapacity(c ErlClient) (*ErlCapacityGuard, bool, error) { var cg ErlCapacityGuard var q capacityQueue var err error var exists bool - var enableCM bool + var isCMEnabled bool // get the client's queue erl.clientLock.RLock() q, exists = erl.capacityByClient[c] - enableCM = erl.enableCM + isCMEnabled = erl.enableCM erl.clientLock.RUnlock() // Step 0: Check for, and create a capacity reservation if needed @@ -187,14 +187,14 @@ func (erl *ElasticRateLimiter) ConsumeCapacity(c ErlClient) (*ErlCapacityGuard, if !exists && erl.CapacityPerReservation > 0 { q, err = erl.openReservation(c) if err != nil { - return nil, err + return nil, isCMEnabled, err } // if the client has been given a new reservation, make sure it cleans up OnClose c.OnClose(func() { erl.closeReservation(c) }) // if this reservation is newly created, directly (blocking) take a capacity q.blockingConsume() - return &ErlCapacityGuard{cq: q, cm: erl.cm}, nil + return &ErlCapacityGuard{cq: q, cm: erl.cm}, isCMEnabled, nil } // Step 1: Attempt consumption from the reserved queue if one exists @@ -204,29 +204,29 @@ func (erl *ElasticRateLimiter) ConsumeCapacity(c ErlClient) (*ErlCapacityGuard, if erl.cm != nil { erl.cm.Consumed(c, time.Now()) // notify the congestion manager that this client consumed from this queue } - return &cg, nil + return &cg, isCMEnabled, nil } } // Step 2: Potentially gate shared queue access if the congestion manager disallows it if erl.cm != nil && - enableCM && + isCMEnabled && erl.cm.ShouldDrop(c) { if erl.congestionControlCounter != nil { erl.congestionControlCounter.Inc(nil) } - return nil, errConManDropped + return nil, isCMEnabled, errConManDropped } // Step 3: Attempt consumption from the shared queue cg, err = erl.sharedCapacity.consume(erl.cm) if err != nil { - return nil, err + return nil, isCMEnabled, err } if erl.cm != nil { erl.cm.Consumed(c, time.Now()) // notify the congestion manager that this client consumed from this queue } - return &cg, nil + return &cg, isCMEnabled, nil } // openReservation creates an entry in the ElasticRateLimiter's reservedCapacity map, diff --git a/util/rateLimit_test.go b/util/rateLimit_test.go index 2794c9a40a..8888bfcf4c 100644 --- a/util/rateLimit_test.go +++ b/util/rateLimit_test.go @@ -53,7 +53,7 @@ func TestElasticRateLimiterCongestionControlled(t *testing.T) { // give the ERL a congestion controler with well defined behavior for testing erl.cm = mockCongestionControl{} - _, err := erl.ConsumeCapacity(client) + _, _, err := erl.ConsumeCapacity(client) // because the ERL gives capacity to a reservation, and then asynchronously drains capacity from the share, // wait a moment before testing the size of the sharedCapacity time.Sleep(100 * time.Millisecond) @@ -62,18 +62,18 @@ func TestElasticRateLimiterCongestionControlled(t *testing.T) { assert.NoError(t, err) erl.EnableCongestionControl() - _, err = erl.ConsumeCapacity(client) + _, _, err = erl.ConsumeCapacity(client) assert.Equal(t, 0, len(erl.capacityByClient[client])) assert.Equal(t, 1, len(erl.sharedCapacity)) assert.NoError(t, err) - _, err = erl.ConsumeCapacity(client) + _, _, err = erl.ConsumeCapacity(client) assert.Equal(t, 0, len(erl.capacityByClient[client])) assert.Equal(t, 1, len(erl.sharedCapacity)) assert.Error(t, err) erl.DisableCongestionControl() - _, err = erl.ConsumeCapacity(client) + _, _, err = erl.ConsumeCapacity(client) assert.Equal(t, 0, len(erl.capacityByClient[client])) assert.Equal(t, 0, len(erl.sharedCapacity)) assert.NoError(t, err) @@ -85,14 +85,14 @@ func TestReservations(t *testing.T) { client2 := mockClient("client2") erl := NewElasticRateLimiter(4, 1, time.Second, nil) - _, err := erl.ConsumeCapacity(client1) + _, _, err := erl.ConsumeCapacity(client1) // because the ERL gives capacity to a reservation, and then asynchronously drains capacity from the share, // wait a moment before testing the size of the sharedCapacity time.Sleep(100 * time.Millisecond) assert.Equal(t, 1, len(erl.capacityByClient)) assert.NoError(t, err) - _, err = erl.ConsumeCapacity(client2) + _, _, err = erl.ConsumeCapacity(client2) // because the ERL gives capacity to a reservation, and then asynchronously drains capacity from the share, // wait a moment before testing the size of the sharedCapacity time.Sleep(100 * time.Millisecond) @@ -113,12 +113,12 @@ func TestZeroSizeReservations(t *testing.T) { client2 := mockClient("client2") erl := NewElasticRateLimiter(4, 0, time.Second, nil) - _, err := erl.ConsumeCapacity(client1) + _, _, err := erl.ConsumeCapacity(client1) time.Sleep(100 * time.Millisecond) assert.Equal(t, 0, len(erl.capacityByClient)) assert.NoError(t, err) - _, err = erl.ConsumeCapacity(client2) + _, _, err = erl.ConsumeCapacity(client2) time.Sleep(100 * time.Millisecond) assert.Equal(t, 0, len(erl.capacityByClient)) assert.NoError(t, err) @@ -134,7 +134,7 @@ func TestConsumeReleaseCapacity(t *testing.T) { client := mockClient("client") erl := NewElasticRateLimiter(4, 3, time.Second, nil) - c1, err := erl.ConsumeCapacity(client) + c1, _, err := erl.ConsumeCapacity(client) // because the ERL gives capacity to a reservation, and then asynchronously drains capacity from the share, // wait a moment before testing the size of the sharedCapacity time.Sleep(100 * time.Millisecond) @@ -142,23 +142,23 @@ func TestConsumeReleaseCapacity(t *testing.T) { assert.Equal(t, 1, len(erl.sharedCapacity)) assert.NoError(t, err) - _, err = erl.ConsumeCapacity(client) + _, _, err = erl.ConsumeCapacity(client) assert.Equal(t, 1, len(erl.capacityByClient[client])) assert.Equal(t, 1, len(erl.sharedCapacity)) assert.NoError(t, err) - _, err = erl.ConsumeCapacity(client) + _, _, err = erl.ConsumeCapacity(client) assert.Equal(t, 0, len(erl.capacityByClient[client])) assert.Equal(t, 1, len(erl.sharedCapacity)) assert.NoError(t, err) // remember this capacity, as it is a shared capacity - c4, err := erl.ConsumeCapacity(client) + c4, _, err := erl.ConsumeCapacity(client) assert.Equal(t, 0, len(erl.capacityByClient[client])) assert.Equal(t, 0, len(erl.sharedCapacity)) assert.NoError(t, err) - _, err = erl.ConsumeCapacity(client) + _, _, err = erl.ConsumeCapacity(client) assert.Equal(t, 0, len(erl.capacityByClient[client])) assert.Equal(t, 0, len(erl.sharedCapacity)) assert.Error(t, err) From c837e495181a900a394da17b99618e21c832853f Mon Sep 17 00:00:00 2001 From: cce <51567+cce@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:06:47 -0500 Subject: [PATCH 2/6] testing: add config flag for ARL to count all messages (#6168) --- config/localTemplate.go | 6 ++++++ config/local_defaults.go | 1 + data/txHandler.go | 8 ++++++++ installer/config.json.example | 1 + test/testdata/configs/config-v35.json | 1 + 5 files changed, 17 insertions(+) diff --git a/config/localTemplate.go b/config/localTemplate.go index 413d48a05a..5975e5fd37 100644 --- a/config/localTemplate.go +++ b/config/localTemplate.go @@ -255,6 +255,12 @@ type Local struct { // EnableTxBacklogAppRateLimiting controls if an app rate limiter should be attached to the tx backlog enqueue process EnableTxBacklogAppRateLimiting bool `version[32]:"true"` + // TxBacklogAppRateLimitingCountERLDrops feeds messages dropped by the ERL congestion manager & rate limiter (enabled by + // EnableTxBacklogRateLimiting) to the app rate limiter (enabled by EnableTxBacklogAppRateLimiting), so that all TX messages + // are counted. This provides more accurate rate limiting for the app rate limiter, at the potential expense of additional + // deserialization overhead. + TxBacklogAppRateLimitingCountERLDrops bool `version[35]:"false"` + // EnableTxBacklogRateLimiting controls if a rate limiter and congestion manager should be attached to the tx backlog enqueue process // if enabled, the over-all TXBacklog Size will be larger by MAX_PEERS*TxBacklogReservedCapacityPerPeer EnableTxBacklogRateLimiting bool `version[27]:"false" version[30]:"true"` diff --git a/config/local_defaults.go b/config/local_defaults.go index 7464853bb4..a58d86f681 100644 --- a/config/local_defaults.go +++ b/config/local_defaults.go @@ -148,6 +148,7 @@ var defaultLocal = Local{ TrackerDBDir: "", TransactionSyncDataExchangeRate: 0, TransactionSyncSignificantMessageThreshold: 0, + TxBacklogAppRateLimitingCountERLDrops: false, TxBacklogAppTxPerSecondRate: 100, TxBacklogAppTxRateLimiterMaxSize: 1048576, TxBacklogRateLimitingCongestionPct: 50, diff --git a/data/txHandler.go b/data/txHandler.go index 3b1cbe5e08..11f2a7fe5a 100644 --- a/data/txHandler.go +++ b/data/txHandler.go @@ -132,6 +132,7 @@ type TxHandler struct { erl *util.ElasticRateLimiter appLimiter *appRateLimiter appLimiterBacklogThreshold int + appLimiterCountERLDrops bool // batchVerifier provides synchronous verification of transaction groups, used only by pubsub validation in validateIncomingTxMessage. batchVerifier verify.TxnGroupBatchSigVerifier @@ -209,6 +210,7 @@ func MakeTxHandler(opts TxHandlerOpts) (*TxHandler, error) { ) // set appLimiter triggering threshold at 50% of the base backlog size handler.appLimiterBacklogThreshold = int(float64(opts.Config.TxBacklogSize) * float64(opts.Config.TxBacklogRateLimitingCongestionPct) / 100) + handler.appLimiterCountERLDrops = opts.Config.TxBacklogAppRateLimitingCountERLDrops } } @@ -743,6 +745,12 @@ func (handler *TxHandler) processIncomingTxn(rawmsg network.IncomingMessage) net }() if shouldDrop { + if handler.appLimiterCountERLDrops { + // decode and let ARL count this txgroup, even though ERL is dropping it + if unverifiedTxGroup, _, invalid := decodeMsg(rawmsg.Data); !invalid { + handler.incomingTxGroupAppRateLimit(unverifiedTxGroup, rawmsg.Sender) + } + } // this TX message was rate-limited by ERL return network.OutgoingMessage{Action: network.Ignore} } diff --git a/installer/config.json.example b/installer/config.json.example index 9d3146b6cb..ea63e89903 100644 --- a/installer/config.json.example +++ b/installer/config.json.example @@ -127,6 +127,7 @@ "TrackerDBDir": "", "TransactionSyncDataExchangeRate": 0, "TransactionSyncSignificantMessageThreshold": 0, + "TxBacklogAppRateLimitingCountERLDrops": false, "TxBacklogAppTxPerSecondRate": 100, "TxBacklogAppTxRateLimiterMaxSize": 1048576, "TxBacklogRateLimitingCongestionPct": 50, diff --git a/test/testdata/configs/config-v35.json b/test/testdata/configs/config-v35.json index 9d3146b6cb..ea63e89903 100644 --- a/test/testdata/configs/config-v35.json +++ b/test/testdata/configs/config-v35.json @@ -127,6 +127,7 @@ "TrackerDBDir": "", "TransactionSyncDataExchangeRate": 0, "TransactionSyncSignificantMessageThreshold": 0, + "TxBacklogAppRateLimitingCountERLDrops": false, "TxBacklogAppTxPerSecondRate": 100, "TxBacklogAppTxRateLimiterMaxSize": 1048576, "TxBacklogRateLimitingCongestionPct": 50, From 61865828b3934b9ac608643c4a66b9233c0cdf87 Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy <65323360+algorandskiy@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:09:18 -0500 Subject: [PATCH 3/6] eval: do not disassemble failed app transaction (#6170) --- data/transactions/logic/eval.go | 4 ++-- data/transactions/logic/evalStateful_test.go | 2 +- ledger/apptxn_test.go | 2 +- ledger/boxtxn_test.go | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 4da436a1b6..a05d41748d 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -1027,8 +1027,8 @@ func (err EvalError) Unwrap() error { } func (cx *EvalContext) evalError(err error) error { - pc, det := cx.pcDetails() - details := fmt.Sprintf("pc=%d, opcodes=%s", pc, det) + pc := cx.pc + details := fmt.Sprintf("pc=%d", pc) err = basics.Annotate(err, "pc", pc, diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index abff0ddb83..73ddeb8d02 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -929,7 +929,7 @@ itxn_submit Stack: []any{uint64(777)}, }, }, - "inner-msg": "logic eval error: cannot compare (uint64 to []byte). Details: app=5000, pc=26, opcodes=pushint 100; pushbytes 0x0201 // 0x0201; ==", + "inner-msg": "logic eval error: cannot compare (uint64 to []byte). Details: app=5000, pc=26", "inner-attrs": map[string]any{ "pc": 26, "group-index": 0, diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index a7b3b15214..a970156c3f 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -566,7 +566,7 @@ func TestRekeyActionCloseAccount(t *testing.T) { // do it again, to ensure the lack of authorization is in the right // place, by matching on the opcode that comes before the itxn_submit we // want to know failed (it'll be in the error). - dl.txn(&useacct, "itxn_field Receiver") + dl.txn(&useacct, "logic eval error") }) } diff --git a/ledger/boxtxn_test.go b/ledger/boxtxn_test.go index 7f42beb143..264515baa2 100644 --- a/ledger/boxtxn_test.go +++ b/ledger/boxtxn_test.go @@ -169,28 +169,28 @@ func TestBoxCreate(t *testing.T) { } dl.txn(adam.Args("check", "adam", "\x00\x00")) - dl.txgroup("box_create; assert", adam.Noted("one"), adam.Noted("two")) + dl.txgroup("logic eval error: assert failed", adam.Noted("one"), adam.Noted("two")) bobo := call.Args("create", "bobo") dl.txn(bobo, fmt.Sprintf("invalid Box reference %#x", "bobo")) bobo.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("bobo")}} dl.txn(bobo) - dl.txgroup("box_create; assert", bobo.Noted("one"), bobo.Noted("two")) + dl.txgroup("logic eval error: assert failed", bobo.Noted("one"), bobo.Noted("two")) dl.beginBlock() chaz := call.Args("create", "chaz") chaz.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("chaz")}} dl.txn(chaz) - dl.txn(chaz.Noted("again"), "box_create; assert") + dl.txn(chaz.Noted("again"), "logic eval error: assert failed") dl.endBlock() // new block - dl.txn(chaz.Noted("again"), "box_create; assert") + dl.txn(chaz.Noted("again"), "logic eval error: assert failed") dogg := call.Args("create", "dogg") dogg.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("dogg")}} dl.txn(dogg, "below min") dl.txn(chaz.Args("delete", "chaz")) - dl.txn(chaz.Args("delete", "chaz").Noted("again"), "box_del; assert") + dl.txn(chaz.Args("delete", "chaz").Noted("again"), "logic eval error: assert failed") dl.txn(dogg) dl.txn(bobo.Args("delete", "bobo")) @@ -229,7 +229,7 @@ func TestBoxRecreate(t *testing.T) { create := call.Args("create", "adam", "\x04") // box value size is 4 bytes recreate := call.Args("recreate", "adam", "\x04") - dl.txn(recreate, "box_create; !; assert") + dl.txn(recreate, "logic eval error: assert failed") dl.txn(create) dl.txn(recreate) dl.txn(call.Args("set", "adam", "\x01\x02\x03\x04")) From 64dc085511c15099383aa90f546a8f9f7c2fda29 Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Fri, 15 Nov 2024 13:20:29 -0500 Subject: [PATCH 4/6] set MaxConnectionsPerIP to 8 --- config/localTemplate.go | 2 +- config/local_defaults.go | 2 +- installer/config.json.example | 2 +- test/testdata/configs/config-v35.json | 2 +- .../testdata/nettemplates/RichAccountStateProof.json | 12 +++++------- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/config/localTemplate.go b/config/localTemplate.go index 5975e5fd37..1387b8c6a1 100644 --- a/config/localTemplate.go +++ b/config/localTemplate.go @@ -66,7 +66,7 @@ type Local struct { PublicAddress string `version[0]:""` // MaxConnectionsPerIP is the maximum number of connections allowed per IP address. - MaxConnectionsPerIP int `version[3]:"30" version[27]:"15" version[35]:"4"` + MaxConnectionsPerIP int `version[3]:"30" version[27]:"15" version[35]:"8"` // PeerPingPeriodSeconds is deprecated and unused. PeerPingPeriodSeconds int `version[0]:"0"` diff --git a/config/local_defaults.go b/config/local_defaults.go index a58d86f681..2891184a21 100644 --- a/config/local_defaults.go +++ b/config/local_defaults.go @@ -111,7 +111,7 @@ var defaultLocal = Local{ MaxAcctLookback: 4, MaxBlockHistoryLookback: 0, MaxCatchpointDownloadDuration: 43200000000000, - MaxConnectionsPerIP: 4, + MaxConnectionsPerIP: 8, MinCatchpointFileDownloadBytesPerSecond: 20480, NetAddress: "", NetworkMessageTraceServer: "", diff --git a/installer/config.json.example b/installer/config.json.example index ea63e89903..1e7937d455 100644 --- a/installer/config.json.example +++ b/installer/config.json.example @@ -90,7 +90,7 @@ "MaxAcctLookback": 4, "MaxBlockHistoryLookback": 0, "MaxCatchpointDownloadDuration": 43200000000000, - "MaxConnectionsPerIP": 4, + "MaxConnectionsPerIP": 8, "MinCatchpointFileDownloadBytesPerSecond": 20480, "NetAddress": "", "NetworkMessageTraceServer": "", diff --git a/test/testdata/configs/config-v35.json b/test/testdata/configs/config-v35.json index ea63e89903..1e7937d455 100644 --- a/test/testdata/configs/config-v35.json +++ b/test/testdata/configs/config-v35.json @@ -90,7 +90,7 @@ "MaxAcctLookback": 4, "MaxBlockHistoryLookback": 0, "MaxCatchpointDownloadDuration": 43200000000000, - "MaxConnectionsPerIP": 4, + "MaxConnectionsPerIP": 8, "MinCatchpointFileDownloadBytesPerSecond": 20480, "NetAddress": "", "NetworkMessageTraceServer": "", diff --git a/test/testdata/nettemplates/RichAccountStateProof.json b/test/testdata/nettemplates/RichAccountStateProof.json index 341bb04d0b..578fb0e3c5 100644 --- a/test/testdata/nettemplates/RichAccountStateProof.json +++ b/test/testdata/nettemplates/RichAccountStateProof.json @@ -14,20 +14,18 @@ "Nodes": [ { "Name": "Relay0", - "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}", "IsRelay": true, "Wallets": [] }, { "Name": "Relay1", - "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}", "IsRelay": true, "Wallets": [] }, - { "Name": "richNode", "Wallets": [ { "Name": "richWallet", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] }, - { "Name": "Node1", "Wallets": [ { "Name": "Wallet1", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] }, - { "Name": "Node2", "Wallets": [ { "Name": "Wallet2", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] }, - { "Name": "Node3", "Wallets": [ { "Name": "Wallet3", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] }, - { "Name": "poorNode", "Wallets": [ { "Name": "poorWallet", "ConfigJSONOverride": "{\"MaxConnectionsPerIP\":8}","ParticipationOnly": false } ] } + { "Name": "richNode", "Wallets": [ { "Name": "richWallet", "ParticipationOnly": false } ] }, + { "Name": "Node1", "Wallets": [ { "Name": "Wallet1", "ParticipationOnly": false } ] }, + { "Name": "Node2", "Wallets": [ { "Name": "Wallet2", "ParticipationOnly": false } ] }, + { "Name": "Node3", "Wallets": [ { "Name": "Wallet3", "ParticipationOnly": false } ] }, + { "Name": "poorNode", "Wallets": [ { "Name": "poorWallet", "ParticipationOnly": false } ] } ] } From cc7f887399087c06121069c142de17dde114ac3d Mon Sep 17 00:00:00 2001 From: cce <51567+cce@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:59:46 -0500 Subject: [PATCH 5/6] eval: add DetailedEvalErrors method to EvalTracer interface (#6172) --- data/pools/transactionPool.go | 6 +++++- data/transactions/logic/eval.go | 12 ++++++++++-- data/transactions/logic/evalStateful_test.go | 3 ++- data/transactions/logic/mocktracer/tracer.go | 3 +++ data/transactions/logic/tracer.go | 13 +++++++++++++ ledger/apptxn_test.go | 2 +- ledger/boxtxn_test.go | 12 ++++++------ ledger/simple_test.go | 2 ++ ledger/simulation/tracer.go | 2 ++ 9 files changed, 44 insertions(+), 11 deletions(-) diff --git a/data/pools/transactionPool.go b/data/pools/transactionPool.go index a2eef08bc3..8446291973 100644 --- a/data/pools/transactionPool.go +++ b/data/pools/transactionPool.go @@ -62,6 +62,7 @@ type TransactionPool struct { cond sync.Cond expiredTxCount map[basics.Round]int pendingBlockEvaluator BlockEvaluator + evalTracer logic.EvalTracer numPendingWholeBlocks basics.Round feePerByte atomic.Uint64 feeThresholdMultiplier uint64 @@ -140,6 +141,9 @@ func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Lo log: log, vac: vac, } + if cfg.EnableDeveloperAPI { + pool.evalTracer = logic.EvalErrorDetailsTracer{} + } pool.cond.L = &pool.mu pool.assemblyCond.L = &pool.assemblyMu pool.recomputeBlockEvaluator(nil, 0) @@ -732,7 +736,7 @@ func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIDs map[transact if hint < 0 || int(knownCommitted) < 0 { hint = 0 } - pool.pendingBlockEvaluator, err = pool.ledger.StartEvaluator(next.BlockHeader, hint, 0, nil) + pool.pendingBlockEvaluator, err = pool.ledger.StartEvaluator(next.BlockHeader, hint, 0, pool.evalTracer) if err != nil { // The pendingBlockEvaluator is an interface, and in case of an evaluator error // we want to remove the interface itself rather then keeping an interface diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index a05d41748d..5edb5b8421 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -1027,8 +1027,16 @@ func (err EvalError) Unwrap() error { } func (cx *EvalContext) evalError(err error) error { - pc := cx.pc - details := fmt.Sprintf("pc=%d", pc) + var pc int + var details string + if cx.Tracer != nil && cx.Tracer.DetailedEvalErrors() { + var det string + pc, det = cx.pcDetails() + details = fmt.Sprintf("pc=%d, opcodes=%s", pc, det) + } else { + pc = cx.pc + details = fmt.Sprintf("pc=%d", pc) + } err = basics.Annotate(err, "pc", pc, diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 73ddeb8d02..36ac82adf1 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -443,6 +443,7 @@ func testApps(t *testing.T, programs []string, txgroup []transactions.SignedTxn, } } ep := NewAppEvalParams(transactions.WrapSignedTxnsWithAD(txgroup), proto, &transactions.SpecialAddresses{}) + ep.Tracer = EvalErrorDetailsTracer{} if ledger == nil { ledger = NewLedger(nil) } @@ -929,7 +930,7 @@ itxn_submit Stack: []any{uint64(777)}, }, }, - "inner-msg": "logic eval error: cannot compare (uint64 to []byte). Details: app=5000, pc=26", + "inner-msg": "logic eval error: cannot compare (uint64 to []byte). Details: app=5000, pc=26, opcodes=pushint 100; pushbytes 0x0201 // 0x0201; ==", "inner-attrs": map[string]any{ "pc": 26, "group-index": 0, diff --git a/data/transactions/logic/mocktracer/tracer.go b/data/transactions/logic/mocktracer/tracer.go index 8ac31cb3a3..deadc0e900 100644 --- a/data/transactions/logic/mocktracer/tracer.go +++ b/data/transactions/logic/mocktracer/tracer.go @@ -232,6 +232,9 @@ func (d *Tracer) AfterBlock(hdr *bookkeeping.BlockHeader) { d.Events = append(d.Events, AfterBlock(hdr.Round)) } +// DetailedEvalErrors returns true, enabling detailed errors in tests. +func (d *Tracer) DetailedEvalErrors() bool { return false } + // copyDeltas makes a deep copy of the given ledgercore.StateDelta pointer, if it's not nil. // This is inefficient, but it should only be used for testing. func copyDeltas(deltas *ledgercore.StateDelta) *ledgercore.StateDelta { diff --git a/data/transactions/logic/tracer.go b/data/transactions/logic/tracer.go index c8996f316c..551ea67e10 100644 --- a/data/transactions/logic/tracer.go +++ b/data/transactions/logic/tracer.go @@ -162,6 +162,10 @@ type EvalTracer interface { // AfterBlock is called after the block has finished evaluation. It will not be called in the event that an evalError // stops evaluation of the block. AfterBlock(hdr *bookkeeping.BlockHeader) + + // DetailedEvalErrors permits the tracer to enable detailed EvalError messages (including PC with disassembled + // opcodes) by returning true. + DetailedEvalErrors() bool } // NullEvalTracer implements EvalTracer, but all of its hook methods do nothing @@ -198,3 +202,12 @@ func (n NullEvalTracer) AfterOpcode(cx *EvalContext, evalError error) {} // AfterBlock does nothing func (n NullEvalTracer) AfterBlock(hdr *bookkeeping.BlockHeader) {} + +// DetailedEvalErrors does nothing +func (n NullEvalTracer) DetailedEvalErrors() bool { return false } + +// EvalErrorDetailsTracer enables disassembled details in EvalError messages, and nothing else. +type EvalErrorDetailsTracer struct{ NullEvalTracer } + +// DetailedEvalErrors returns true. +func (EvalErrorDetailsTracer) DetailedEvalErrors() bool { return true } diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index a970156c3f..a7b3b15214 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -566,7 +566,7 @@ func TestRekeyActionCloseAccount(t *testing.T) { // do it again, to ensure the lack of authorization is in the right // place, by matching on the opcode that comes before the itxn_submit we // want to know failed (it'll be in the error). - dl.txn(&useacct, "logic eval error") + dl.txn(&useacct, "itxn_field Receiver") }) } diff --git a/ledger/boxtxn_test.go b/ledger/boxtxn_test.go index 264515baa2..7f42beb143 100644 --- a/ledger/boxtxn_test.go +++ b/ledger/boxtxn_test.go @@ -169,28 +169,28 @@ func TestBoxCreate(t *testing.T) { } dl.txn(adam.Args("check", "adam", "\x00\x00")) - dl.txgroup("logic eval error: assert failed", adam.Noted("one"), adam.Noted("two")) + dl.txgroup("box_create; assert", adam.Noted("one"), adam.Noted("two")) bobo := call.Args("create", "bobo") dl.txn(bobo, fmt.Sprintf("invalid Box reference %#x", "bobo")) bobo.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("bobo")}} dl.txn(bobo) - dl.txgroup("logic eval error: assert failed", bobo.Noted("one"), bobo.Noted("two")) + dl.txgroup("box_create; assert", bobo.Noted("one"), bobo.Noted("two")) dl.beginBlock() chaz := call.Args("create", "chaz") chaz.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("chaz")}} dl.txn(chaz) - dl.txn(chaz.Noted("again"), "logic eval error: assert failed") + dl.txn(chaz.Noted("again"), "box_create; assert") dl.endBlock() // new block - dl.txn(chaz.Noted("again"), "logic eval error: assert failed") + dl.txn(chaz.Noted("again"), "box_create; assert") dogg := call.Args("create", "dogg") dogg.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("dogg")}} dl.txn(dogg, "below min") dl.txn(chaz.Args("delete", "chaz")) - dl.txn(chaz.Args("delete", "chaz").Noted("again"), "logic eval error: assert failed") + dl.txn(chaz.Args("delete", "chaz").Noted("again"), "box_del; assert") dl.txn(dogg) dl.txn(bobo.Args("delete", "bobo")) @@ -229,7 +229,7 @@ func TestBoxRecreate(t *testing.T) { create := call.Args("create", "adam", "\x04") // box value size is 4 bytes recreate := call.Args("recreate", "adam", "\x04") - dl.txn(recreate, "logic eval error: assert failed") + dl.txn(recreate, "box_create; !; assert") dl.txn(create) dl.txn(recreate) dl.txn(call.Args("set", "adam", "\x01\x02\x03\x04")) diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 8af40eaaf3..9d4a480b49 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -29,6 +29,7 @@ import ( "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/committee" "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/data/txntest" "github.com/algorand/go-algorand/ledger/eval" @@ -91,6 +92,7 @@ func nextBlock(t testing.TB, ledger *Ledger) *eval.BlockEvaluator { eval, err := eval.StartEvaluator(ledger, nextHdr, eval.EvaluatorOptions{ Generate: true, Validate: true, // Do the complete checks that a new txn would be subject to + Tracer: logic.EvalErrorDetailsTracer{}, }) require.NoError(t, err) return eval diff --git a/ledger/simulation/tracer.go b/ledger/simulation/tracer.go index 94fa1394f7..8a9b7dda7b 100644 --- a/ledger/simulation/tracer.go +++ b/ledger/simulation/tracer.go @@ -555,3 +555,5 @@ func (tracer *evalTracer) AfterProgram(cx *logic.EvalContext, pass bool, evalErr } } } + +func (tracer *evalTracer) DetailedEvalErrors() bool { return true } From a7c9fbb6139bd78d935fb9a5791fc6504d5115db Mon Sep 17 00:00:00 2001 From: cce <51567+cce@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:25:53 -0500 Subject: [PATCH 6/6] update mocktracer.DetailedEvalErrors to match comment --- data/transactions/logic/mocktracer/tracer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/transactions/logic/mocktracer/tracer.go b/data/transactions/logic/mocktracer/tracer.go index deadc0e900..20ed108938 100644 --- a/data/transactions/logic/mocktracer/tracer.go +++ b/data/transactions/logic/mocktracer/tracer.go @@ -233,7 +233,7 @@ func (d *Tracer) AfterBlock(hdr *bookkeeping.BlockHeader) { } // DetailedEvalErrors returns true, enabling detailed errors in tests. -func (d *Tracer) DetailedEvalErrors() bool { return false } +func (d *Tracer) DetailedEvalErrors() bool { return true } // copyDeltas makes a deep copy of the given ledgercore.StateDelta pointer, if it's not nil. // This is inefficient, but it should only be used for testing.