Skip to content

Commit 39bd318

Browse files
committed
Merge branch '311-aggregator-wait-for-receipt-for-1-minute-if-not-bump-the-fee-v2' into test-aggregator-bump-fee
2 parents 4a8a9f5 + 3469101 commit 39bd318

File tree

6 files changed

+166
-64
lines changed

6 files changed

+166
-64
lines changed

aggregator/internal/pkg/aggregator.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,8 @@ func (agg *Aggregator) sendAggregatedResponse(batchIdentifierHash [32]byte, batc
301301
"senderAddress", hex.EncodeToString(senderAddress[:]),
302302
"batchIdentifierHash", hex.EncodeToString(batchIdentifierHash[:]))
303303

304-
receipt, err := agg.avsWriter.SendAggregatedResponse(batchIdentifierHash, batchMerkleRoot, senderAddress, nonSignerStakesAndSignature)
304+
onRetry := func() { agg.metrics.IncBumpedGasPriceForAggregatedResponse() }
305+
receipt, err := agg.avsWriter.SendAggregatedResponse(batchIdentifierHash, batchMerkleRoot, senderAddress, nonSignerStakesAndSignature, onRetry)
305306
if err != nil {
306307
agg.walletMutex.Unlock()
307308
agg.logger.Infof("- Unlocked Wallet Resources: Error sending aggregated response for batch %s. Error: %s", hex.EncodeToString(batchIdentifierHash[:]), err)

core/chainio/avs_writer.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import (
2020
"github.com/yetanotherco/aligned_layer/core/utils"
2121
)
2222

23+
const (
24+
gasBumpPercentage int = 10
25+
)
26+
2327
type AvsWriter struct {
2428
*avsregistry.ChainWriter
2529
AvsContractBindings *AvsServiceBindings
@@ -72,7 +76,10 @@ func NewAvsWriterFromConfig(baseConfig *config.BaseConfig, ecdsaConfig *config.E
7276
}, nil
7377
}
7478

75-
func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMerkleRoot [32]byte, senderAddress [20]byte, nonSignerStakesAndSignature servicemanager.IBLSSignatureCheckerNonSignerStakesAndSignature) (*types.Receipt, error) {
79+
// Sends AggregatedResponse and waits for the receipt for three blocks, if not received
80+
// it will try again bumping the last tx gas price based on `CalculateGasPriceBump`
81+
// This process happens indefinitely until the transaction is included.
82+
func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMerkleRoot [32]byte, senderAddress [20]byte, nonSignerStakesAndSignature servicemanager.IBLSSignatureCheckerNonSignerStakesAndSignature, onRetry func()) (*types.Receipt, error) {
7683
txOpts := *w.Signer.GetTxOpts()
7784
txOpts.NoSend = true // simulate the transaction
7885
tx, err := w.AvsContractBindings.ServiceManager.RespondToTaskV2(&txOpts, batchMerkleRoot, senderAddress, nonSignerStakesAndSignature, new(big.Int).SetUint64(5))
@@ -94,10 +101,20 @@ func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMe
94101
txNonce := new(big.Int).SetUint64(tx.Nonce())
95102
txOpts.NoSend = false
96103
txOpts.Nonce = txNonce
97-
var i uint64 = 1
98104

99-
executeTransaction := func(bumpedGasPrices *big.Int) (*types.Transaction, error) {
100-
txOpts.GasPrice = bumpedGasPrices
105+
lastTxGasPrice := tx.GasPrice()
106+
var i uint64
107+
i = 0
108+
sendTransaction := func() (*types.Receipt, error) {
109+
if i > 0 {
110+
onRetry()
111+
}
112+
i++
113+
114+
bumpedGasPrice := utils.CalculateGasPriceBump(lastTxGasPrice, gasBumpPercentage)
115+
lastTxGasPrice = bumpedGasPrice
116+
txOpts.GasPrice = bumpedGasPrice
117+
101118
w.logger.Infof("Sending ResponseToTask transaction with a gas price of %v", txOpts.GasPrice)
102119
err = w.checkRespondToTaskFeeLimit(tx, txOpts, batchIdentifierHash, senderAddress)
103120

@@ -112,12 +129,24 @@ func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMe
112129
if err != nil {
113130
return nil, connection.PermanentError{Inner: err}
114131
}
115-
return tx, nil
116132
}
117-
return tx, nil
133+
134+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*36)
135+
defer cancel()
136+
receipt, err := utils.WaitForTransactionReceipt(w.Client, ctx, tx.Hash())
137+
138+
if receipt != nil {
139+
return receipt, nil
140+
}
141+
// if we are here, this means we have reached the timeout (after three blocks it hasn't been included)
142+
// so we try again by bumping the fee to make sure its included
143+
if err != nil {
144+
return nil, err
145+
}
146+
return nil, fmt.Errorf("transaction failed")
118147
}
119148

120-
return utils.SendTransactionWithInfiniteRetryAndBumpingGasPrice(executeTransaction, w.Client, tx.GasPrice())
149+
return connection.RetryWithData(sendTransaction, 1000, 2, 0)
121150
}
122151

123152
func (w *AvsWriter) checkRespondToTaskFeeLimit(tx *types.Transaction, txOpts bind.TransactOpts, batchIdentifierHash [32]byte, senderAddress [20]byte) error {

core/connection.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ func (e PermanentError) Is(err error) bool {
2121

2222
// Same as Retry only that the functionToRetry can return a value upon correct execution
2323
func RetryWithData[T any](functionToRetry func() (*T, error), minDelay uint64, factor float64, maxTries uint64) (*T, error) {
24-
i := 0
2524
f := func() (*T, error) {
2625
val, err := functionToRetry()
27-
i++
2826
if perm, ok := err.(PermanentError); err != nil && ok {
2927
return nil, backoff.Permanent(perm.Inner)
3028
}
@@ -54,10 +52,8 @@ func RetryWithData[T any](functionToRetry func() (*T, error), minDelay uint64, f
5452
// The function to be retried should return `PermanentError` when the condition for stop retrying
5553
// is met.
5654
func Retry(functionToRetry func() error, minDelay uint64, factor float64, maxTries uint64) error {
57-
i := 0
5855
f := func() error {
5956
err := functionToRetry()
60-
i++
6157
if perm, ok := err.(PermanentError); err != nil && ok {
6258
return backoff.Permanent(perm.Inner)
6359
}

core/utils/eth_client_utils.go

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
eigentypes "github.com/Layr-Labs/eigensdk-go/types"
1212
gethcommon "github.com/ethereum/go-ethereum/common"
1313
"github.com/ethereum/go-ethereum/core/types"
14-
connection "github.com/yetanotherco/aligned_layer/core"
1514
)
1615

1716
const maxRetries = 25
@@ -50,45 +49,12 @@ func BytesToQuorumThresholdPercentages(quorumThresholdPercentagesBytes []byte) e
5049
return quorumThresholdPercentages
5150
}
5251

53-
// Very basic algorithm to calculate the gasPrice bump based on the currentGasPrice and retry iteration.
54-
// It adds a i/10 percentage to the current prices, where i represents the iteration.
55-
func CalculateGasPriceBumpBasedOnRetry(currentGasPrice *big.Int, iteration int) *big.Int {
56-
factor := (new(big.Int).Add(big.NewInt(100), new(big.Int).Mul(big.NewInt(int64(iteration)), big.NewInt(10))))
57-
gasPrice := new(big.Int).Mul(currentGasPrice, factor)
58-
gasPrice = gasPrice.Div(gasPrice, big.NewInt(100))
52+
// Very basic algorithm to calculate the gasPrice bump based on the currentGasPrice and percentage.
53+
// It adds a the percentage to the current gas price.
54+
func CalculateGasPriceBump(currentGasPrice *big.Int, percentage int) *big.Int {
55+
percentageBump := new(big.Int).Div(big.NewInt(int64(percentage)), big.NewInt(100))
56+
bumpAmount := new(big.Int).Mul(currentGasPrice, percentageBump)
57+
bumpedGasPrice := new(big.Int).Add(currentGasPrice, bumpAmount)
5958

60-
return gasPrice
61-
}
62-
63-
// Sends a transaction and waits for the receipt for three blocks, if not received
64-
// it will try again bumping the gas price based on `CalculateGasPriceBumpBasedOnRetry`
65-
// and pass it to executeTransaction (make sure you update the txOpts with the new gasPrice)
66-
// This process happens indefinitely until we get the receipt or the receipt status is an err.
67-
func SendTransactionWithInfiniteRetryAndBumpingGasPrice(executeTransaction func(*big.Int) (*types.Transaction, error), client eth.InstrumentedClient, baseGasPrice *big.Int) (*types.Receipt, error) {
68-
i := 0
69-
sendTransaction := func() (*types.Receipt, error) {
70-
i++
71-
gasPrice := CalculateGasPriceBumpBasedOnRetry(baseGasPrice, i)
72-
73-
tx, err := executeTransaction(gasPrice)
74-
if err != nil {
75-
return nil, err
76-
}
77-
78-
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*2000)
79-
defer cancel()
80-
receipt, err := WaitForTransactionReceipt(client, ctx, tx.Hash())
81-
82-
if receipt != nil {
83-
return receipt, nil
84-
}
85-
// if we are here, this means we have reached the timeout (after three blocks it hasn't been included)
86-
// so we try again by bumping the fee to make sure its included
87-
if err != nil {
88-
return nil, err
89-
}
90-
return nil, fmt.Errorf("transaction failed")
91-
92-
}
93-
return connection.RetryWithData(sendTransaction, 1000, 2, 0)
59+
return bumpedGasPrice
9460
}

grafana/provisioning/dashboards/aligned/aggregator_batcher.json

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@
758758
"h": 7,
759759
"w": 10,
760760
"x": 0,
761-
"y": 34
761+
"y": 20
762762
},
763763
"id": 9,
764764
"options": {
@@ -910,7 +910,7 @@
910910
"h": 7,
911911
"w": 10,
912912
"x": 10,
913-
"y": 34
913+
"y": 20
914914
},
915915
"id": 1,
916916
"options": {
@@ -974,7 +974,7 @@
974974
"h": 7,
975975
"w": 5,
976976
"x": 0,
977-
"y": 41
977+
"y": 27
978978
},
979979
"id": 8,
980980
"options": {
@@ -1044,7 +1044,7 @@
10441044
"h": 7,
10451045
"w": 5,
10461046
"x": 5,
1047-
"y": 41
1047+
"y": 27
10481048
},
10491049
"id": 7,
10501050
"options": {
@@ -1083,6 +1083,106 @@
10831083
"title": "Tasks Received",
10841084
"type": "stat"
10851085
},
1086+
{
1087+
"datasource": {
1088+
"type": "prometheus",
1089+
"uid": "prometheus"
1090+
},
1091+
"description": "Number of times gas price was bumped while sending an aggregated response.",
1092+
"fieldConfig": {
1093+
"defaults": {
1094+
"color": {
1095+
"mode": "palette-classic"
1096+
},
1097+
"custom": {
1098+
"axisCenteredZero": false,
1099+
"axisColorMode": "text",
1100+
"axisLabel": "",
1101+
"axisPlacement": "auto",
1102+
"barAlignment": 0,
1103+
"drawStyle": "line",
1104+
"fillOpacity": 0,
1105+
"gradientMode": "none",
1106+
"hideFrom": {
1107+
"legend": false,
1108+
"tooltip": false,
1109+
"viz": false
1110+
},
1111+
"insertNulls": false,
1112+
"lineInterpolation": "linear",
1113+
"lineWidth": 1,
1114+
"pointSize": 5,
1115+
"scaleDistribution": {
1116+
"type": "linear"
1117+
},
1118+
"showPoints": "auto",
1119+
"spanNulls": false,
1120+
"stacking": {
1121+
"group": "A",
1122+
"mode": "none"
1123+
},
1124+
"thresholdsStyle": {
1125+
"mode": "off"
1126+
}
1127+
},
1128+
"mappings": [],
1129+
"thresholds": {
1130+
"mode": "absolute",
1131+
"steps": [
1132+
{
1133+
"color": "green",
1134+
"value": null
1135+
},
1136+
{
1137+
"color": "red",
1138+
"value": 80
1139+
}
1140+
]
1141+
}
1142+
},
1143+
"overrides": []
1144+
},
1145+
"gridPos": {
1146+
"h": 7,
1147+
"w": 10,
1148+
"x": 10,
1149+
"y": 27
1150+
},
1151+
"id": 20,
1152+
"interval": "36",
1153+
"options": {
1154+
"legend": {
1155+
"calcs": [],
1156+
"displayMode": "list",
1157+
"placement": "bottom",
1158+
"showLegend": true
1159+
},
1160+
"tooltip": {
1161+
"mode": "single",
1162+
"sort": "none"
1163+
}
1164+
},
1165+
"targets": [
1166+
{
1167+
"datasource": {
1168+
"type": "prometheus",
1169+
"uid": "prometheus"
1170+
},
1171+
"disableTextWrap": false,
1172+
"editorMode": "builder",
1173+
"expr": "aligned_respond_to_task_gas_price_bumped",
1174+
"fullMetaSearch": false,
1175+
"includeNullMetadata": true,
1176+
"instant": false,
1177+
"legendFormat": "__auto",
1178+
"range": true,
1179+
"refId": "A",
1180+
"useBackend": false
1181+
}
1182+
],
1183+
"title": "Bumped gas price for aggregated responses",
1184+
"type": "timeseries"
1185+
},
10861186
{
10871187
"datasource": {
10881188
"type": "prometheus",
@@ -1109,7 +1209,7 @@
11091209
"h": 7,
11101210
"w": 5,
11111211
"x": 0,
1112-
"y": 48
1212+
"y": 34
11131213
},
11141214
"id": 2,
11151215
"options": {
@@ -1179,7 +1279,7 @@
11791279
"h": 7,
11801280
"w": 5,
11811281
"x": 5,
1182-
"y": 48
1282+
"y": 34
11831283
},
11841284
"id": 5,
11851285
"options": {

metrics/metrics.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ import (
1212
)
1313

1414
type Metrics struct {
15-
ipPortAddress string
16-
logger logging.Logger
17-
numAggregatedResponses prometheus.Counter
18-
numAggregatorReceivedTasks prometheus.Counter
19-
numOperatorTaskResponses prometheus.Counter
15+
ipPortAddress string
16+
logger logging.Logger
17+
numAggregatedResponses prometheus.Counter
18+
numAggregatorReceivedTasks prometheus.Counter
19+
numOperatorTaskResponses prometheus.Counter
20+
numBumpedGasPriceForAggregatedResponse prometheus.Counter
2021
}
2122

2223
const alignedNamespace = "aligned"
@@ -40,6 +41,11 @@ func NewMetrics(ipPortAddress string, reg prometheus.Registerer, logger logging.
4041
Name: "aggregator_received_tasks",
4142
Help: "Number of tasks received by the Service Manager",
4243
}),
44+
numBumpedGasPriceForAggregatedResponse: promauto.With(reg).NewCounter(prometheus.CounterOpts{
45+
Namespace: alignedNamespace,
46+
Name: "respond_to_task_gas_price_bumped",
47+
Help: "Number of times gas price was bumped while sending aggregated response",
48+
}),
4349
}
4450
}
4551

@@ -74,3 +80,7 @@ func (m *Metrics) IncAggregatedResponses() {
7480
func (m *Metrics) IncOperatorTaskResponses() {
7581
m.numOperatorTaskResponses.Inc()
7682
}
83+
84+
func (m *Metrics) IncBumpedGasPriceForAggregatedResponse() {
85+
m.numBumpedGasPriceForAggregatedResponse.Inc()
86+
}

0 commit comments

Comments
 (0)