From 484b27202e60d4960da59668f29318aa937c4924 Mon Sep 17 00:00:00 2001 From: Diego Campo Date: Sat, 30 Nov 2024 22:08:15 +0100 Subject: [PATCH] fee mgmt + apiAdapter fix/refactor --- .env.example | 2 +- README.md | 29 +++++++++------------ adapter/api/apiadapter/README.md | 4 +-- adapter/api/apiadapter/main.go | 2 +- adapter_factory.go | 6 ++--- config.example.json | 10 +++---- lib/repo_tx_utils.go | 18 +++++++++---- usecase/build_commit_worker_payload_test.go | 12 ++++----- 8 files changed, 43 insertions(+), 40 deletions(-) diff --git a/.env.example b/.env.example index f638b3d..6ff2115 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -ALLORA_OFFCHAIN_NODE_CONFIG_JSON='{"wallet":{"addressKeyName":"test-offchain","addressRestoreMnemonic":"surge verify input...","alloraHomeDir":"","gas":"auto","gasAdjustment":1.5,"gasPrices":0.08,"maxFees":200000,"nodeRpc":"http://localhost:26657","maxRetries":3,"retryDelay":3,"accountSequenceRetryDelay":5,"submitTx":true,"blockDurationEstimated":10,"windowCorrectionFactor":0.8},"worker":[{"topicId":1,"inferenceEntrypointName":"api-worker-reputer","loopSeconds":5,"parameters":{"InferenceEndpoint":"http://localhost:8000/inference/{Token}","Token":"ETH"}}]}' +ALLORA_OFFCHAIN_NODE_CONFIG_JSON='{"wallet":{"addressKeyName":"test-offchain","addressRestoreMnemonic":"surge verify input...","alloraHomeDir":"","gas":"auto","gasAdjustment":1.2,"gasPrices":"auto","maxFees":500000,"nodeRpc":"http://localhost:26657","maxRetries":3,"retryDelay":3,"accountSequenceRetryDelay":5,"submitTx":true,"blockDurationEstimated":10,"windowCorrectionFactor":0.8},"worker":[{"topicId":1,"inferenceEntrypointName":"api-worker-reputer","loopSeconds":5,"parameters":{"InferenceEndpoint":"http://localhost:8000/inference/{Token}","Token":"ETH"}}]}' diff --git a/README.md b/README.md index b0ee51c..65626d6 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,8 @@ The wallet configuration is done in `config.json` under the `wallet` field. - `gas` can be set to `auto` or a specific gas value. If set to `auto`, the node will automatically calculate the gas limit based on the estimated gas used by the transactions. - `gasAdjustment` is used to adjust the gas limit. -- `gasPrices` and `maxFees` fields are used to set the gas prices and max fees for the wallet. They are expressed in `uallo`. +- `gasPrices` can be set to `auto` or a specific gas price. If set to `auto`, the node will automatically calculate the gas price based on chain's feemarket-based gas prices. This is the recommended setting, since feemarket introduces variability in the gas price in the chain. +- `maxFees` set the max fees that can be paid for a transaction. They are expressed numerically in `uallo`. ### Error handling @@ -184,8 +185,7 @@ These below are excerpts of the configuration (with some parts omitted for brevi "worker": [ { "topicId": 1, - "inferenceEntrypointName": "api-worker-reputer", - "loopSeconds": 10, + "inferenceEntrypointName": "apiAdapter", "parameters": { "InferenceEndpoint": "http://source:8000/inference/{Token}", "Token": "ETH" @@ -201,8 +201,7 @@ These below are excerpts of the configuration (with some parts omitted for brevi "worker": [ { "topicId": 1, - "forecastEntrypointName": "api-worker-reputer", - "loopSeconds": 10, + "forecastEntrypointName": "apiAdapter", "parameters": { "ForecastEndpoint": "http://source:8000/forecasts/{TopicId}/{BlockHeight}" } @@ -219,9 +218,8 @@ These below are excerpts of the configuration (with some parts omitted for brevi "worker": [ { "topicId": 1, - "inferenceEntrypointName": "api-worker-reputer", - "forecastEntrypointName": "api-worker-reputer", - "loopSeconds": 10, + "inferenceEntrypointName": "apiAdapter", + "forecastEntrypointName": "apiAdapter", "parameters": { "InferenceEndpoint": "http://source:8000/inference/{Token}", "ForecastEndpoint": "http://source:8000/forecasts/{TopicId}/{BlockHeight}", @@ -239,9 +237,8 @@ These below are excerpts of the configuration (with some parts omitted for brevi "reputer": [ { "topicId": 1, - "groundTruthEntrypointName": "api-worker-reputer", - "lossFunctionEntrypointName": "api-worker-reputer", - "loopSeconds": 30, + "groundTruthEntrypointName": "apiAdapter", + "lossFunctionEntrypointName": "apiAdapter", "minStake": 100000, "groundTruthParameters": { "GroundTruthEndpoint": "http://localhost:8888/gt/{Token}/{BlockHeight}", @@ -265,9 +262,8 @@ These below are excerpts of the configuration (with some parts omitted for brevi "worker": [ { "topicId": 1, - "inferenceEntrypointName": "api-worker-reputer", - "forecastEntrypointName": "api-worker-reputer", - "loopSeconds": 10, + "inferenceEntrypointName": "apiAdapter", + "forecastEntrypointName": "apiAdapter", "parameters": { "InferenceEndpoint": "http://source:8000/inference/{Token}", "ForecastEndpoint": "http://source:8000/forecasts/{TopicId}/{BlockHeight}", @@ -278,9 +274,8 @@ These below are excerpts of the configuration (with some parts omitted for brevi "reputer": [ { "topicId": 1, - "groundTruthEntrypointName": "api-worker-reputer", - "lossFunctionEntrypointName": "api-worker-reputer", - "loopSeconds": 30, + "groundTruthEntrypointName": "apiAdapter", + "lossFunctionEntrypointName": "apiAdapter", "minStake": 100000, "groundTruthParameters": { "GroundTruthEndpoint": "http://localhost:8888/gt/{Token}/{BlockHeight}", diff --git a/adapter/api/apiadapter/README.md b/adapter/api/apiadapter/README.md index ecd71b4..419f526 100644 --- a/adapter/api/apiadapter/README.md +++ b/adapter/api/apiadapter/README.md @@ -30,8 +30,8 @@ Example as Reputer ("gt" in this context means "ground truth"): Reputer: []lib.ReputerConfig{ { "topicId": 1, - "groundTruthEntrypointName": "api-worker-reputer", - "lossFunctionEntrypointName": "api-worker-reputer", + "groundTruthEntrypointName": "apiAdapter", + "lossFunctionEntrypointName": "apiAdapter", "loopSeconds": 30, "minStake": 100000, "groundTruthParameters": { diff --git a/adapter/api/apiadapter/main.go b/adapter/api/apiadapter/main.go index c5251ab..96a09ac 100644 --- a/adapter/api/apiadapter/main.go +++ b/adapter/api/apiadapter/main.go @@ -278,7 +278,7 @@ func (a *AlloraAdapter) CanSourceGroundTruthAndComputeLoss() bool { func NewAlloraAdapter() *AlloraAdapter { return &AlloraAdapter{ - name: "api-worker-reputer", + name: "apiAdapter", } } diff --git a/adapter_factory.go b/adapter_factory.go index 99c7cb4..20e6d8c 100644 --- a/adapter_factory.go +++ b/adapter_factory.go @@ -1,15 +1,15 @@ package main import ( - apiAdapter "allora_offchain_node/adapter/api/apiadapter" + apiadapter "allora_offchain_node/adapter/api/apiadapter" lib "allora_offchain_node/lib" "fmt" ) func NewAlloraAdapter(name string) (lib.AlloraAdapter, error) { switch name { - case "api-worker-reputer": - return apiAdapter.NewAlloraAdapter(), nil + case "apiAdapter": + return apiadapter.NewAlloraAdapter(), nil // Add other cases for different adapters here default: return nil, fmt.Errorf("unknown adapter name: %s", name) diff --git a/config.example.json b/config.example.json index 3c97338..41c4a18 100644 --- a/config.example.json +++ b/config.example.json @@ -4,9 +4,9 @@ "addressRestoreMnemonic": "your mnemonic here", "alloraHomeDir": "", "gas": "auto", - "gasAdjustment": 1.5, + "gasAdjustment": 1.2, "gasPrices": "auto", - "maxFees": 2000000, + "maxFees": 500000, "nodeRpc": "https://allora-rpc.testnet.allora.network", "maxRetries": 5, "retryDelay": 3, @@ -18,7 +18,7 @@ "worker": [ { "topicId": 1, - "inferenceEntrypointName": "api-worker-reputer", + "inferenceEntrypointName": "apiAdapter", "parameters": { "InferenceEndpoint": "http://source:8000/inference/{Token}", "Token": "ETH" @@ -28,8 +28,8 @@ "reputer": [ { "topicId": 1, - "groundTruthEntrypointName": "api-worker-reputer", - "lossFunctionEntrypointName": "api-worker-reputer", + "groundTruthEntrypointName": "apiAdapter", + "lossFunctionEntrypointName": "apiAdapter", "minStake": 100000, "groundTruthParameters": { "GroundTruthEndpoint": "http://localhost:8888/gt/{Token}/{BlockHeight}", diff --git a/lib/repo_tx_utils.go b/lib/repo_tx_utils.go index cea2f56..663f28e 100644 --- a/lib/repo_tx_utils.go +++ b/lib/repo_tx_utils.go @@ -77,8 +77,16 @@ func processError(err error, infoMsg string, retryCount int64, node *NodeConfig) time.Sleep(time.Duration(node.Wallet.AccountSequenceRetryDelay) * time.Second) return ERROR_PROCESSING_CONTINUE, nil case int(sdkerrors.ErrInsufficientFee.ABCICode()): + log.Info(). + Err(err). + Str("msg", infoMsg). + Msg("Insufficient fees") return ERROR_PROCESSING_FEES, nil case int(feemarkettypes.ErrNoFeeCoins.ABCICode()): + log.Info(). + Err(err). + Str("msg", infoMsg). + Msg("No fee coins") return ERROR_PROCESSING_FEES, nil case int(sdkerrors.ErrTxTooLarge.ABCICode()): return ERROR_PROCESSING_ERROR, errorsmod.Wrapf(err, "tx too large") @@ -166,7 +174,7 @@ func (node *NodeConfig) SendDataWithRetry(ctx context.Context, req sdktypes.Msg, } excessFactorFees := float64(EXCESS_CORRECTION_IN_GAS) * gasPrices // Keep track of how many times fees need to be recalculated to avoid missing fee info between errors - recalculateFees := 0 + recalculateFees := 1 // Use to keep track of expected sequence number between errors globalExpectedSeqNum := uint64(0) @@ -178,7 +186,7 @@ func (node *NodeConfig) SendDataWithRetry(ctx context.Context, req sdktypes.Msg, for retryCount := int64(0); retryCount <= node.Wallet.MaxRetries; retryCount++ { log.Debug().Msgf("SendDataWithRetry iteration started (%d/%d)", retryCount, node.Wallet.MaxRetries) - // Create tx without fees + // Create tx without fees to simulate tx creation and get estimated gas and seq number txOptions := cosmosclient.TxOptions{} // nolint: exhaustruct if globalExpectedSeqNum > 0 && node.Chain.Client.TxFactory.Sequence() != globalExpectedSeqNum { log.Debug(). @@ -228,7 +236,6 @@ func (node *NodeConfig) SendDataWithRetry(ctx context.Context, req sdktypes.Msg, case ERROR_PROCESSING_FEES: // Error has not been handled, just mark as recalculate fees on this iteration log.Debug().Msg("Marking fee recalculation on tx creation") - recalculateFees += 1 case ERROR_PROCESSING_FAILURE: return nil, errorsmod.Wrapf(err, "tx failed and not retried") default: @@ -240,9 +247,10 @@ func (node *NodeConfig) SendDataWithRetry(ctx context.Context, req sdktypes.Msg, } // Handle fees if necessary - if gasPrices > 0 && recalculateFees > 0 { + if gasPrices > 0 { // Precalculate fees - fees := uint64(float64(txService.Gas()+EXCESS_CORRECTION_IN_GAS) * gasPrices) + estimatedGas := float64(txService.Gas()) * node.Wallet.GasAdjustment + fees := uint64(float64(estimatedGas+EXCESS_CORRECTION_IN_GAS) * gasPrices) // Add excess fees correction factor to increase with each fee-problematic retry fees = fees + uint64(float64(recalculateFees)*excessFactorFees) // Limit fees to maxFees diff --git a/usecase/build_commit_worker_payload_test.go b/usecase/build_commit_worker_payload_test.go index 6b13467..b6d2499 100644 --- a/usecase/build_commit_worker_payload_test.go +++ b/usecase/build_commit_worker_payload_test.go @@ -34,8 +34,8 @@ func TestComputeWorkerBundle(t *testing.T) { workerConfig: lib.WorkerResponse{ WorkerConfig: lib.WorkerConfig{ TopicId: emissionstypes.TopicId(1), - InferenceEntrypointName: "api-worker-reputer", - ForecastEntrypointName: "api-worker-reputer", + InferenceEntrypointName: "apiAdapter", + ForecastEntrypointName: "apiAdapter", InferenceEntrypoint: nil, // Will be set in the test ForecastEntrypoint: nil, // Will be set in the test Parameters: workerOptions, @@ -75,8 +75,8 @@ func TestComputeWorkerBundle(t *testing.T) { workerConfig: lib.WorkerResponse{ WorkerConfig: lib.WorkerConfig{ TopicId: emissionstypes.TopicId(1), - InferenceEntrypointName: "api-worker-reputer", - ForecastEntrypointName: "api-worker-reputer", + InferenceEntrypointName: "apiAdapter", + ForecastEntrypointName: "apiAdapter", InferenceEntrypoint: nil, ForecastEntrypoint: nil, Parameters: workerOptions, @@ -96,8 +96,8 @@ func TestComputeWorkerBundle(t *testing.T) { workerConfig: lib.WorkerResponse{ WorkerConfig: lib.WorkerConfig{ TopicId: emissionstypes.TopicId(1), - InferenceEntrypointName: "api-worker-reputer", - ForecastEntrypointName: "api-worker-reputer", + InferenceEntrypointName: "apiAdapter", + ForecastEntrypointName: "apiAdapter", InferenceEntrypoint: nil, ForecastEntrypoint: nil, Parameters: workerOptions,