mirror of
https://github.com/0glabs/0g-chain.git
synced 2025-04-04 15:55:23 +00:00
Compare commits
1 Commits
21343f1505
...
0a31f108df
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0a31f108df |
@ -2,7 +2,10 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/0glabs/0g-chain/chaincfg"
|
||||||
"github.com/cockroachdb/errors"
|
"github.com/cockroachdb/errors"
|
||||||
abci "github.com/cometbft/cometbft/abci/types"
|
abci "github.com/cometbft/cometbft/abci/types"
|
||||||
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -13,6 +16,8 @@ import (
|
|||||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const gasPriceSuggestionBlockNum int64 = 5
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// GasTx defines the contract that a transaction with a gas limit must implement.
|
// GasTx defines the contract that a transaction with a gas limit must implement.
|
||||||
GasTx interface {
|
GasTx interface {
|
||||||
@ -33,14 +38,26 @@ type (
|
|||||||
mempool mempool.Mempool
|
mempool mempool.Mempool
|
||||||
txVerifier ProposalTxVerifier
|
txVerifier ProposalTxVerifier
|
||||||
txSelector TxSelector
|
txSelector TxSelector
|
||||||
|
feemarketKeeper FeeMarketKeeper
|
||||||
|
}
|
||||||
|
FeeMarketKeeper interface {
|
||||||
|
SetSuggestionGasPrice(ctx sdk.Context, gas *big.Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
txnInfo struct {
|
||||||
|
gasPrice *big.Int
|
||||||
|
gasLimit uint64
|
||||||
|
nonce uint64
|
||||||
|
sender string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDefaultProposalHandler(mp mempool.Mempool, txVerifier ProposalTxVerifier) *DefaultProposalHandler {
|
func NewDefaultProposalHandler(mp mempool.Mempool, txVerifier ProposalTxVerifier, feemarketKeeper FeeMarketKeeper) *DefaultProposalHandler {
|
||||||
return &DefaultProposalHandler{
|
return &DefaultProposalHandler{
|
||||||
mempool: mp,
|
mempool: mp,
|
||||||
txVerifier: txVerifier,
|
txVerifier: txVerifier,
|
||||||
txSelector: NewDefaultTxSelector(),
|
txSelector: NewDefaultTxSelector(),
|
||||||
|
feemarketKeeper: feemarketKeeper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,11 +114,14 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
|
|||||||
return abci.ResponsePrepareProposal{Txs: h.txSelector.SelectedTxs()}
|
return abci.ResponsePrepareProposal{Txs: h.txSelector.SelectedTxs()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
txnInfoMap := make(map[string][]*txnInfo, h.mempool.CountTx())
|
||||||
|
|
||||||
iterator := h.mempool.Select(ctx, req.Txs)
|
iterator := h.mempool.Select(ctx, req.Txs)
|
||||||
selectedTxsSignersSeqs := make(map[string]uint64)
|
selectedTxsSignersSeqs := make(map[string]uint64)
|
||||||
var selectedTxsNums int
|
var selectedTxsNums int
|
||||||
for iterator != nil {
|
for iterator != nil {
|
||||||
memTx := iterator.Tx()
|
memTx := iterator.Tx()
|
||||||
|
|
||||||
sigs, err := memTx.(signing.SigVerifiableTx).GetSignaturesV2()
|
sigs, err := memTx.(signing.SigVerifiableTx).GetSignaturesV2()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to get signatures: %w", err))
|
panic(fmt.Errorf("failed to get signatures: %w", err))
|
||||||
@ -134,10 +154,42 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
|
|||||||
txSignersSeqs[signer] = nonce
|
txSignersSeqs[signer] = nonce
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, exists := txnInfoMap[signer]; !exists {
|
||||||
|
txnInfoMap[signer] = make([]*txnInfo, 0, 128)
|
||||||
|
}
|
||||||
|
|
||||||
|
txnInfoMap[signer] = append(txnInfoMap[signer], &txnInfo{
|
||||||
|
gasPrice: ethTx.GasPrice(),
|
||||||
|
gasLimit: ethTx.Gas(),
|
||||||
|
nonce: nonce,
|
||||||
|
sender: signer,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// ignore multisig case now
|
||||||
|
fee := memTx.(sdk.Fee)
|
||||||
|
if len(sigs) == 1 {
|
||||||
|
signer := sdk.AccAddress(sigs[0].PubKey.Address()).String()
|
||||||
|
|
||||||
|
if _, exists := txnInfoMap[signer]; !exists {
|
||||||
|
txnInfoMap[signer] = make([]*txnInfo, 0, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
evmGasPrice, err := utilCosmosDemonGasPriceToEvmDemonGasPrice(fee.GetAmount())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
txnInfoMap[signer] = append(txnInfoMap[signer], &txnInfo{
|
||||||
|
gasPrice: evmGasPrice,
|
||||||
|
gasLimit: utilCosmosDemonGasLimitToEvmDemonGasLimit(fee.GetGas()),
|
||||||
|
nonce: sigs[0].Sequence,
|
||||||
|
sender: signer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, sig := range sigs {
|
for _, sig := range sigs {
|
||||||
signer := sdk.AccAddress(sig.PubKey.Address()).String()
|
signer := sdk.AccAddress(sig.PubKey.Address()).String()
|
||||||
seq, ok := selectedTxsSignersSeqs[signer]
|
seq, ok := selectedTxsSignersSeqs[signer]
|
||||||
@ -198,6 +250,65 @@ func (h *DefaultProposalHandler) PrepareProposalHandler() sdk.PrepareProposalHan
|
|||||||
|
|
||||||
iterator = iterator.Next()
|
iterator = iterator.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(txnInfoMap) == 0 {
|
||||||
|
h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(0))
|
||||||
|
} else {
|
||||||
|
senderCnt := 0
|
||||||
|
txnCnt := 0
|
||||||
|
for sender := range txnInfoMap {
|
||||||
|
sort.Slice(txnInfoMap[sender], func(i, j int) bool {
|
||||||
|
return txnInfoMap[sender][i].nonce < txnInfoMap[sender][j].nonce
|
||||||
|
})
|
||||||
|
txnCnt += len(txnInfoMap[sender])
|
||||||
|
senderCnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
remaing := gasPriceSuggestionBlockNum * int64(maxBlockGas)
|
||||||
|
var lastProcessedTx *txnInfo
|
||||||
|
|
||||||
|
for remaing > 0 && len(txnInfoMap) > 0 {
|
||||||
|
// Find the highest gas price among the first transaction of each account
|
||||||
|
var highestGasPrice *big.Int
|
||||||
|
var selectedSender string
|
||||||
|
|
||||||
|
// Compare first transaction (lowest nonce) from each account
|
||||||
|
for sender, txns := range txnInfoMap {
|
||||||
|
if len(txns) == 0 {
|
||||||
|
delete(txnInfoMap, sender)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// First tx has lowest nonce due to earlier sorting
|
||||||
|
if highestGasPrice == nil || txns[0].gasPrice.Cmp(highestGasPrice) > 0 {
|
||||||
|
highestGasPrice = txns[0].gasPrice
|
||||||
|
selectedSender = sender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if selectedSender == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the selected transaction
|
||||||
|
selectedTx := txnInfoMap[selectedSender][0]
|
||||||
|
remaing -= int64(selectedTx.gasLimit)
|
||||||
|
lastProcessedTx = selectedTx
|
||||||
|
|
||||||
|
// Remove processed transaction
|
||||||
|
txnInfoMap[selectedSender] = txnInfoMap[selectedSender][1:]
|
||||||
|
if len(txnInfoMap[selectedSender]) == 0 {
|
||||||
|
delete(txnInfoMap, selectedSender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastProcessedTx != nil && remaing <= 0 {
|
||||||
|
h.feemarketKeeper.SetSuggestionGasPrice(ctx, lastProcessedTx.gasPrice)
|
||||||
|
} else {
|
||||||
|
h.feemarketKeeper.SetSuggestionGasPrice(ctx, big.NewInt(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return abci.ResponsePrepareProposal{Txs: h.txSelector.SelectedTxs()}
|
return abci.ResponsePrepareProposal{Txs: h.txSelector.SelectedTxs()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,3 +447,22 @@ func (ts *defaultTxSelector) SelectTxForProposal(maxTxBytes, maxBlockGas uint64,
|
|||||||
// check if we've reached capacity; if so, we cannot select any more transactions
|
// check if we've reached capacity; if so, we cannot select any more transactions
|
||||||
return ts.totalTxBytes >= maxTxBytes || (maxBlockGas > 0 && (ts.totalTxGas >= maxBlockGas))
|
return ts.totalTxBytes >= maxTxBytes || (maxBlockGas > 0 && (ts.totalTxGas >= maxBlockGas))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func utilCosmosDemonGasPriceToEvmDemonGasPrice(gasGroup sdk.Coins) (*big.Int, error) {
|
||||||
|
gasPrice := big.NewInt(0)
|
||||||
|
for _, coin := range gasGroup {
|
||||||
|
if coin.Denom == chaincfg.GasDenom {
|
||||||
|
thisGasPrice := big.NewInt(0).SetUint64(coin.Amount.Uint64())
|
||||||
|
thisGasPrice = thisGasPrice.Mul(thisGasPrice, big.NewInt(0).SetInt64(chaincfg.GasDenomConversionMultiplier))
|
||||||
|
gasPrice = gasPrice.Add(gasPrice, thisGasPrice)
|
||||||
|
} else {
|
||||||
|
return big.NewInt(0), fmt.Errorf("invalid denom: %s", coin.Denom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gasPrice, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func utilCosmosDemonGasLimitToEvmDemonGasLimit(gasLimit uint64) uint64 {
|
||||||
|
return gasLimit * chaincfg.GasDenomConversionMultiplier
|
||||||
|
}
|
||||||
|
@ -62,6 +62,7 @@ func TestAppAnteHandler_AuthorizedMempool(t *testing.T) {
|
|||||||
encodingConfig,
|
encodingConfig,
|
||||||
baseapp.SetChainID(app.TestChainId),
|
baseapp.SetChainID(app.TestChainId),
|
||||||
),
|
),
|
||||||
|
nil,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,6 +313,7 @@ func NewApp(
|
|||||||
encodingConfig chainparams.EncodingConfig,
|
encodingConfig chainparams.EncodingConfig,
|
||||||
options Options,
|
options Options,
|
||||||
bApp *baseapp.BaseApp,
|
bApp *baseapp.BaseApp,
|
||||||
|
setAbciProposalHandler func(feemarketKeeper FeeMarketKeeper) sdk.PrepareProposalHandler,
|
||||||
) *App {
|
) *App {
|
||||||
appCodec := encodingConfig.Marshaler
|
appCodec := encodingConfig.Marshaler
|
||||||
legacyAmino := encodingConfig.Amino
|
legacyAmino := encodingConfig.Amino
|
||||||
@ -489,7 +490,6 @@ func NewApp(
|
|||||||
keys[feemarkettypes.StoreKey],
|
keys[feemarkettypes.StoreKey],
|
||||||
tkeys[feemarkettypes.TransientKey],
|
tkeys[feemarkettypes.TransientKey],
|
||||||
feemarketSubspace,
|
feemarketSubspace,
|
||||||
bApp.Mempool(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
app.evmutilKeeper = evmutilkeeper.NewKeeper(
|
app.evmutilKeeper = evmutilkeeper.NewKeeper(
|
||||||
@ -902,6 +902,10 @@ func NewApp(
|
|||||||
app.SetBeginBlocker(app.BeginBlocker)
|
app.SetBeginBlocker(app.BeginBlocker)
|
||||||
app.SetEndBlocker(app.EndBlocker)
|
app.SetEndBlocker(app.EndBlocker)
|
||||||
|
|
||||||
|
if setAbciProposalHandler != nil {
|
||||||
|
abciProposalHandler := setAbciProposalHandler(app.feeMarketKeeper)
|
||||||
|
bApp.SetPrepareProposal(abciProposalHandler)
|
||||||
|
}
|
||||||
// load store
|
// load store
|
||||||
if !options.SkipLoadLatest {
|
if !options.SkipLoadLatest {
|
||||||
if err := app.LoadLatestVersion(); err != nil {
|
if err := app.LoadLatestVersion(); err != nil {
|
||||||
|
@ -38,6 +38,7 @@ func TestNewApp(t *testing.T) {
|
|||||||
MakeEncodingConfig(),
|
MakeEncodingConfig(),
|
||||||
baseapp.SetChainID(TestChainId),
|
baseapp.SetChainID(TestChainId),
|
||||||
),
|
),
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +56,7 @@ func TestExport(t *testing.T) {
|
|||||||
MakeEncodingConfig(),
|
MakeEncodingConfig(),
|
||||||
baseapp.SetChainID(TestChainId),
|
baseapp.SetChainID(TestChainId),
|
||||||
),
|
),
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
genesisState := GenesisStateWithSingleValidator(&TestApp{App: *app}, NewDefaultGenesisState())
|
genesisState := GenesisStateWithSingleValidator(&TestApp{App: *app}, NewDefaultGenesisState())
|
||||||
|
@ -96,7 +96,7 @@ func NewTestAppFromSealed() TestApp {
|
|||||||
bApp := NewBaseApp(log.NewNopLogger(), db, encCfg, baseapp.SetChainID(TestChainId))
|
bApp := NewBaseApp(log.NewNopLogger(), db, encCfg, baseapp.SetChainID(TestChainId))
|
||||||
app := NewApp(
|
app := NewApp(
|
||||||
chaincfg.DefaultNodeHome, nil,
|
chaincfg.DefaultNodeHome, nil,
|
||||||
encCfg, DefaultOptions, bApp,
|
encCfg, DefaultOptions, bApp, nil,
|
||||||
)
|
)
|
||||||
return TestApp{App: *app}
|
return TestApp{App: *app}
|
||||||
}
|
}
|
||||||
|
@ -126,8 +126,6 @@ func (ac appCreator) newApp(
|
|||||||
baseapp.SetMempool(mempool),
|
baseapp.SetMempool(mempool),
|
||||||
)
|
)
|
||||||
bApp.SetTxEncoder(ac.encodingConfig.TxConfig.TxEncoder())
|
bApp.SetTxEncoder(ac.encodingConfig.TxConfig.TxEncoder())
|
||||||
abciProposalHandler := app.NewDefaultProposalHandler(mempool, bApp)
|
|
||||||
bApp.SetPrepareProposal(abciProposalHandler.PrepareProposalHandler())
|
|
||||||
|
|
||||||
newApp := app.NewApp(
|
newApp := app.NewApp(
|
||||||
homeDir, traceStore, ac.encodingConfig,
|
homeDir, traceStore, ac.encodingConfig,
|
||||||
@ -142,6 +140,10 @@ func (ac appCreator) newApp(
|
|||||||
EVMMaxGasWanted: cast.ToUint64(appOpts.Get(ethermintflags.EVMMaxTxGasWanted)),
|
EVMMaxGasWanted: cast.ToUint64(appOpts.Get(ethermintflags.EVMMaxTxGasWanted)),
|
||||||
},
|
},
|
||||||
bApp,
|
bApp,
|
||||||
|
func(k app.FeeMarketKeeper) sdk.PrepareProposalHandler {
|
||||||
|
abciProposalHandler := app.NewDefaultProposalHandler(mempool, bApp, k)
|
||||||
|
return abciProposalHandler.PrepareProposalHandler()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return newApp
|
return newApp
|
||||||
@ -170,14 +172,14 @@ func (ac appCreator) appExport(
|
|||||||
var tempApp *app.App
|
var tempApp *app.App
|
||||||
if height != -1 {
|
if height != -1 {
|
||||||
bApp := app.NewBaseApp(logger, db, ac.encodingConfig)
|
bApp := app.NewBaseApp(logger, db, ac.encodingConfig)
|
||||||
tempApp = app.NewApp(homePath, traceStore, ac.encodingConfig, options, bApp)
|
tempApp = app.NewApp(homePath, traceStore, ac.encodingConfig, options, bApp, nil)
|
||||||
|
|
||||||
if err := tempApp.LoadHeight(height); err != nil {
|
if err := tempApp.LoadHeight(height); err != nil {
|
||||||
return servertypes.ExportedApp{}, err
|
return servertypes.ExportedApp{}, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bApp := app.NewBaseApp(logger, db, ac.encodingConfig)
|
bApp := app.NewBaseApp(logger, db, ac.encodingConfig)
|
||||||
tempApp = app.NewApp(homePath, traceStore, ac.encodingConfig, options, bApp)
|
tempApp = app.NewApp(homePath, traceStore, ac.encodingConfig, options, bApp, nil)
|
||||||
}
|
}
|
||||||
return tempApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
|
return tempApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
|
||||||
}
|
}
|
||||||
|
2
go.mod
2
go.mod
@ -250,7 +250,7 @@ replace (
|
|||||||
// TODO: Tag before release
|
// TODO: Tag before release
|
||||||
github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc2
|
github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc2
|
||||||
// Use ethermint fork that respects min-gas-price with NoBaseFee true and london enabled, and includes eip712 support
|
// Use ethermint fork that respects min-gas-price with NoBaseFee true and london enabled, and includes eip712 support
|
||||||
github.com/evmos/ethermint => github.com/0glabs/ethermint v0.21.0-0g.v3.1.12
|
github.com/evmos/ethermint => github.com/0glabs/ethermint v0.21.0-0g.v3.1.10
|
||||||
// See https://github.com/cosmos/cosmos-sdk/pull/10401, https://github.com/cosmos/cosmos-sdk/commit/0592ba6158cd0bf49d894be1cef4faeec59e8320
|
// See https://github.com/cosmos/cosmos-sdk/pull/10401, https://github.com/cosmos/cosmos-sdk/commit/0592ba6158cd0bf49d894be1cef4faeec59e8320
|
||||||
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.0
|
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.0
|
||||||
// Downgraded to avoid bugs in following commits which causes "version does not exist" errors
|
// Downgraded to avoid bugs in following commits which causes "version does not exist" errors
|
||||||
|
4
go.sum
4
go.sum
@ -213,8 +213,8 @@ github.com/0glabs/cometbft v0.37.9-0glabs.1 h1:KQJG17Y21suKP3QNICLto4b5Ak73XbSmK
|
|||||||
github.com/0glabs/cometbft v0.37.9-0glabs.1/go.mod h1:j0Q3RqrCd+cztWCugs3obbzC4NyHGBPZZjtm/fWV00I=
|
github.com/0glabs/cometbft v0.37.9-0glabs.1/go.mod h1:j0Q3RqrCd+cztWCugs3obbzC4NyHGBPZZjtm/fWV00I=
|
||||||
github.com/0glabs/cosmos-sdk v0.47.10-0glabs.10 h1:NJp0RwczHBO4EvrQdDxxftHOgUDBtNh7M/vpaG7wFtQ=
|
github.com/0glabs/cosmos-sdk v0.47.10-0glabs.10 h1:NJp0RwczHBO4EvrQdDxxftHOgUDBtNh7M/vpaG7wFtQ=
|
||||||
github.com/0glabs/cosmos-sdk v0.47.10-0glabs.10/go.mod h1:KskIVnhXTFqrw7CDccMvx7To5KzUsOomIsQV7sPGOog=
|
github.com/0glabs/cosmos-sdk v0.47.10-0glabs.10/go.mod h1:KskIVnhXTFqrw7CDccMvx7To5KzUsOomIsQV7sPGOog=
|
||||||
github.com/0glabs/ethermint v0.21.0-0g.v3.1.12 h1:IRVTFhDEH2J5w8ywQW7obXQxYhJYib70SNgKqLOXikU=
|
github.com/0glabs/ethermint v0.21.0-0g.v3.1.10 h1:DE8hK2OJp9rKYdT2Y0G259/6nAFcOQi383l2e7/VFnE=
|
||||||
github.com/0glabs/ethermint v0.21.0-0g.v3.1.12/go.mod h1:6e/gOcDLhvlDWK3JLJVBgki0gD6H4E1eG7l9byocgWA=
|
github.com/0glabs/ethermint v0.21.0-0g.v3.1.10/go.mod h1:6e/gOcDLhvlDWK3JLJVBgki0gD6H4E1eG7l9byocgWA=
|
||||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
||||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
|
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM=
|
||||||
|
Loading…
Reference in New Issue
Block a user