From 43e9f277d4c42b224f83870586150ff5c02a8ced Mon Sep 17 00:00:00 2001 From: Makram Date: Wed, 13 Dec 2023 22:01:04 +0200 Subject: [PATCH 01/15] feat: add liquidity balancer specs table (#11564) Add the liquidity balancer specs table. This table will be used to house the specs for the liquidity balancer job which will be implemented in ccip rather than core. The entirety of the spec's content will be in the `liquidity_balancer_config` field. This is done to minimize future migrations. This doesn't mean we won't be doing strict checks on the contents of the config during the validation process. --- core/services/job/models.go | 8 +++ .../0213_liquidity_balancer_specs.sql | 49 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 core/store/migrate/migrations/0213_liquidity_balancer_specs.sql diff --git a/core/services/job/models.go b/core/services/job/models.go index b21ecc12e72..3c869f76056 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -152,6 +152,8 @@ type Job struct { GatewaySpecID *int32 EALSpec *EALSpec EALSpecID *int32 + LiquidityBalancerSpec *LiquidityBalancerSpec + LiquidityBalancerSpecID *int32 PipelineSpecID int32 PipelineSpec *pipeline.Spec JobSpecErrors []SpecError @@ -793,3 +795,9 @@ type EALSpec struct { // UpdatedAt is the time this job was last updated. UpdatedAt time.Time `toml:"-"` } + +type LiquidityBalancerSpec struct { + ID int32 + + LiquidityBalancerConfig string `toml:"liquidityBalancerConfig" db:"liquidity_balancer_config"` +} diff --git a/core/store/migrate/migrations/0213_liquidity_balancer_specs.sql b/core/store/migrate/migrations/0213_liquidity_balancer_specs.sql new file mode 100644 index 00000000000..cd717181f58 --- /dev/null +++ b/core/store/migrate/migrations/0213_liquidity_balancer_specs.sql @@ -0,0 +1,49 @@ +-- +goose Up +CREATE TABLE liquidity_balancer_specs ( + id BIGSERIAL PRIMARY KEY, + liquidity_balancer_config JSONB NOT NULL +); + +ALTER TABLE + jobs +ADD COLUMN + liquidity_balancer_spec_id BIGINT REFERENCES liquidity_balancer_specs(id), +DROP CONSTRAINT chk_only_one_spec, +ADD CONSTRAINT chk_only_one_spec CHECK ( + num_nonnulls( + ocr_oracle_spec_id, ocr2_oracle_spec_id, + direct_request_spec_id, flux_monitor_spec_id, + keeper_spec_id, cron_spec_id, webhook_spec_id, + vrf_spec_id, blockhash_store_spec_id, + block_header_feeder_spec_id, bootstrap_spec_id, + gateway_spec_id, + legacy_gas_station_server_spec_id, + legacy_gas_station_sidecar_spec_id, + eal_spec_id, + liquidity_balancer_spec_id + ) = 1 +); + +-- +goose Down +ALTER TABLE + jobs +DROP CONSTRAINT chk_only_one_spec, +ADD CONSTRAINT chk_only_one_spec CHECK ( + num_nonnulls( + ocr_oracle_spec_id, ocr2_oracle_spec_id, + direct_request_spec_id, flux_monitor_spec_id, + keeper_spec_id, cron_spec_id, webhook_spec_id, + vrf_spec_id, blockhash_store_spec_id, + block_header_feeder_spec_id, bootstrap_spec_id, + gateway_spec_id, + legacy_gas_station_server_spec_id, + legacy_gas_station_sidecar_spec_id, + eal_spec_id + ) = 1 +); +ALTER TABLE + jobs +DROP COLUMN + liquidity_balancer_spec_id; +DROP TABLE + liquidity_balancer_specs; From 19c7ccac1714dae991d7b3aecc864f99d722b2d4 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 13 Dec 2023 12:07:31 -0800 Subject: [PATCH 02/15] Auto 8087 modular streams (#11489) * make streams lookup modular * polish * address comment to use pointer instead of array/map * rebase * get rid of slice * keep using array and index * remove duplicate logging * address err re-declarion lint issues --- core/scripts/chaincli/handler/debug.go | 223 +++++--- .../handler/mercury_lookup_handler.go | 536 ------------------ core/scripts/go.mod | 3 +- core/scripts/go.sum | 2 - .../v21/mercury/streams/streams.go | 105 ++-- .../v21/mercury/streams/streams_test.go | 20 +- 6 files changed, 222 insertions(+), 667 deletions(-) delete mode 100644 core/scripts/chaincli/handler/mercury_lookup_handler.go diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 5947337b187..815a0c9a030 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -18,13 +18,19 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" + "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" @@ -35,12 +41,7 @@ import ( const ( ConditionTrigger uint8 = iota LogTrigger - - blockNumber = "blockNumber" expectedTypeAndVersion = "KeeperRegistry 2.1.0" - feedIdHex = "feedIdHex" - feedIDs = "feedIDs" - timestamp = "timestamp" ) var packer = encoding.NewAbiPacker() @@ -51,24 +52,29 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if len(args) < 1 { failCheckArgs("no upkeepID supplied", nil) } + // test that we are connected to an archive node _, err := k.client.BalanceAt(ctx, gethcommon.Address{}, big.NewInt(1)) if err != nil { failCheckConfig("you are not connected to an archive node; try using infura or alchemy", err) } + chainIDBig, err := k.client.ChainID(ctx) if err != nil { failUnknown("unable to retrieve chainID from rpc client", err) } chainID := chainIDBig.Int64() + + var triggerCallOpts *bind.CallOpts // use latest block for conditionals, but use block from tx for log triggers + latestCallOpts := &bind.CallOpts{Context: ctx} // always use latest block + // connect to registry contract - latestCallOpts := &bind.CallOpts{Context: ctx} // always use latest block - triggerCallOpts := &bind.CallOpts{Context: ctx} // use latest block for conditionals, but use block from tx for log triggers registryAddress := gethcommon.HexToAddress(k.cfg.RegistryAddress) keeperRegistry21, err := iregistry21.NewIKeeperRegistryMaster(registryAddress, k.client) if err != nil { failUnknown("failed to connect to registry contract", err) } + // verify contract is correct typeAndVersion, err := keeperRegistry21.TypeAndVersion(latestCallOpts) if err != nil { @@ -124,17 +130,21 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { var checkResult iregistry21.CheckUpkeep var blockNum uint64 var performData []byte + var workID [32]byte + var trigger ocr2keepers.Trigger upkeepNeeded := false // check upkeep if triggerType == ConditionTrigger { message("upkeep identified as conditional trigger") - tmpCheckResult, err := keeperRegistry21.CheckUpkeep0(latestCallOpts, upkeepID) + var tmpCheckResult iregistry21.CheckUpkeep0 + tmpCheckResult, err = keeperRegistry21.CheckUpkeep0(latestCallOpts, upkeepID) if err != nil { failUnknown("failed to check upkeep: ", err) } checkResult = iregistry21.CheckUpkeep(tmpCheckResult) // do tenderly simulation - rawCall, err := core.RegistryABI.Pack("checkUpkeep", upkeepID, []byte{}) + var rawCall []byte + rawCall, err = core.RegistryABI.Pack("checkUpkeep", upkeepID, []byte{}) if err != nil { failUnknown("failed to pack raw checkUpkeep call", err) } @@ -146,19 +156,26 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { failCheckArgs("txHash and log index must be supplied to command in order to debug log triggered upkeeps", nil) } txHash := gethcommon.HexToHash(args[1]) - logIndex, err := strconv.ParseInt(args[2], 10, 64) + + var logIndex int64 + logIndex, err = strconv.ParseInt(args[2], 10, 64) if err != nil { failCheckArgs("unable to parse log index", err) } - // find transaction receipt - _, isPending, err := k.client.TransactionByHash(ctx, txHash) + + // check that tx is confirmed + var isPending bool + _, isPending, err = k.client.TransactionByHash(ctx, txHash) if err != nil { log.Fatal("failed to get tx by hash", err) } if isPending { resolveIneligible(fmt.Sprintf("tx %s is still pending confirmation", txHash)) } - receipt, err := k.client.TransactionReceipt(ctx, txHash) + + // find transaction receipt + var receipt *types.Receipt + receipt, err = k.client.TransactionReceipt(ctx, txHash) if err != nil { failCheckArgs("failed to fetch tx receipt", err) } @@ -176,9 +193,11 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } // check that tx for this upkeep / tx was not already performed message(fmt.Sprintf("LogTrigger{blockNum: %d, blockHash: %s, txHash: %s, logIndex: %d}", blockNum, receipt.BlockHash.Hex(), txHash, logIndex)) - workID := mustUpkeepWorkID(upkeepID, blockNum, receipt.BlockHash, txHash, logIndex) + trigger = mustAutomationTrigger(txHash, logIndex, blockNum, receipt.BlockHash) + workID = mustUpkeepWorkID(upkeepID, trigger) message(fmt.Sprintf("workID computed: %s", hex.EncodeToString(workID[:]))) - hasKey, err := keeperRegistry21.HasDedupKey(latestCallOpts, workID) + var hasKey bool + hasKey, err = keeperRegistry21.HasDedupKey(latestCallOpts, workID) if err != nil { failUnknown("failed to check if upkeep was already performed: ", err) } @@ -186,11 +205,13 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { resolveIneligible("upkeep was already performed") } triggerCallOpts = &bind.CallOpts{Context: ctx, BlockNumber: big.NewInt(receipt.BlockNumber.Int64())} - rawTriggerConfig, err := keeperRegistry21.GetUpkeepTriggerConfig(triggerCallOpts, upkeepID) + var rawTriggerConfig []byte + rawTriggerConfig, err = keeperRegistry21.GetUpkeepTriggerConfig(triggerCallOpts, upkeepID) if err != nil { failUnknown("failed to fetch trigger config for upkeep", err) } - triggerConfig, err := packer.UnpackLogTriggerConfig(rawTriggerConfig) + var triggerConfig automation_utils_2_1.LogTriggerConfig + triggerConfig, err = packer.UnpackLogTriggerConfig(rawTriggerConfig) if err != nil { failUnknown("failed to unpack trigger config", err) } @@ -200,11 +221,13 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if !logMatchesTriggerConfig(triggeringEvent, triggerConfig) { resolveIneligible("log does not match trigger config") } - header, err := k.client.HeaderByHash(ctx, receipt.BlockHash) + var header *types.Header + header, err = k.client.HeaderByHash(ctx, receipt.BlockHash) if err != nil { failUnknown("failed to find block", err) } - triggerData, err := packTriggerData(triggeringEvent, header.Time) + var triggerData []byte + triggerData, err = packTriggerData(triggeringEvent, header.Time) if err != nil { failUnknown("failed to pack trigger data", err) } @@ -213,7 +236,8 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { failUnknown("failed to check upkeep", err) } // do tenderly simulations - rawCall, err := core.RegistryABI.Pack("checkUpkeep", upkeepID, triggerData) + var rawCall []byte + rawCall, err = core.RegistryABI.Pack("checkUpkeep", upkeepID, triggerData) if err != nil { failUnknown("failed to pack raw checkUpkeep call", err) } @@ -228,73 +252,88 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if checkResult.UpkeepFailureReason != 0 { message(fmt.Sprintf("checkUpkeep failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) } + if checkResult.UpkeepFailureReason == uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { - // TODO use the new streams lookup lib - //mc := &models.MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, k.cfg.MercuryKey} - //mercuryConfig := evm.NewMercuryConfig(mc, core.StreamsCompatibleABI) - //lggr, _ := logger.NewLogger() - //blockSub := &blockSubscriber{k.client} - //_ = streams.NewStreamsLookup(packer, mercuryConfig, blockSub, keeperRegistry21, k.rpcClient, lggr) - - streamsLookupErr, err := packer.DecodeStreamsLookupRequest(checkResult.PerformData) + mc := &models.MercuryCredentials{LegacyURL: k.cfg.MercuryLegacyURL, URL: k.cfg.MercuryURL, Username: k.cfg.MercuryID, Password: k.cfg.MercuryKey} + mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) + lggr, _ := logger.NewLogger() + blockSub := &blockSubscriber{k.client} + streams := streams.NewStreamsLookup(packer, mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) + + var streamsLookupErr *mercury.StreamsLookupError + streamsLookupErr, err = packer.DecodeStreamsLookupRequest(checkResult.PerformData) if err == nil { message("upkeep reverted with StreamsLookup") message(fmt.Sprintf("StreamsLookup data: {FeedParamKey: %s, Feeds: %v, TimeParamKey: %s, Time: %d, ExtraData: %s}", streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time.Uint64(), hexutil.Encode(streamsLookupErr.ExtraData))) - if streamsLookupErr.FeedParamKey == feedIdHex && streamsLookupErr.TimeParamKey == blockNumber { + + streamsLookup := &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: streamsLookupErr.FeedParamKey, + Feeds: streamsLookupErr.Feeds, + TimeParamKey: streamsLookupErr.TimeParamKey, + Time: streamsLookupErr.Time, + ExtraData: streamsLookupErr.ExtraData, + }, + UpkeepId: upkeepID, + Block: blockNum, + } + + if streamsLookup.IsMercuryV02() { message("using mercury lookup v0.2") - // handle v0.2 - cfg, err := keeperRegistry21.GetUpkeepPrivilegeConfig(triggerCallOpts, upkeepID) + // check if upkeep is allowed to use mercury v0.2 + var allowed bool + _, _, _, allowed, err = streams.AllowedToUseMercury(latestCallOpts, upkeepID) if err != nil { - failUnknown("failed to get upkeep privilege config ", err) - } - allowed := false - if len(cfg) > 0 { - var privilegeConfig streams.UpkeepPrivilegeConfig - if err := json.Unmarshal(cfg, &privilegeConfig); err != nil { - failUnknown("failed to unmarshal privilege config ", err) - } - allowed = privilegeConfig.MercuryEnabled + failUnknown("failed to check if upkeep is allowed to use mercury", err) } if !allowed { resolveIneligible("upkeep reverted with StreamsLookup but is not allowed to access streams") } - } else if streamsLookupErr.FeedParamKey != feedIDs || streamsLookupErr.TimeParamKey != timestamp { + } else if streamsLookup.IsMercuryV03() { // handle v0.3 - resolveIneligible("upkeep reverted with StreamsLookup but the configuration is invalid") - } else { message("using mercury lookup v0.3") + } else { + resolveIneligible("upkeep reverted with StreamsLookup but the configuration is invalid") } - streamsLookup := &StreamsLookup{streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time, streamsLookupErr.ExtraData, upkeepID, blockNum} if k.cfg.MercuryLegacyURL == "" || k.cfg.MercuryURL == "" || k.cfg.MercuryID == "" || k.cfg.MercuryKey == "" { failCheckConfig("Mercury configs not set properly, check your MERCURY_LEGACY_URL, MERCURY_URL, MERCURY_ID and MERCURY_KEY", nil) } - handler := NewMercuryLookupHandler(&MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, k.cfg.MercuryKey}, k.rpcClient) - state, failureReason, values, _, err := handler.doMercuryRequest(ctx, streamsLookup) - if failureReason == UpkeepFailureReasonInvalidRevertDataInput { + + // do mercury request + automationCheckResult := mustAutomationCheckResult(upkeepID, checkResult, trigger) + checkResults := []ocr2keepers.CheckResult{automationCheckResult} + + var values [][]byte + values, err = streams.DoMercuryRequest(ctx, streamsLookup, checkResults, 0) + + if automationCheckResult.IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { resolveIneligible("upkeep used invalid revert data") } - if state == InvalidMercuryRequest { + if automationCheckResult.PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { resolveIneligible("the mercury request data is invalid") } if err != nil { failCheckConfig("failed to do mercury request ", err) } - callbackResult, err := keeperRegistry21.CheckCallback(triggerCallOpts, upkeepID, values, streamsLookup.extraData) + + // do checkCallback + err = streams.CheckCallback(ctx, values, streamsLookup, checkResults, 0) if err != nil { failUnknown("failed to execute mercury callback ", err) } - if callbackResult.UpkeepFailureReason != 0 { - message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) + if automationCheckResult.IneligibilityReason != 0 { + message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", automationCheckResult.IneligibilityReason)) } - upkeepNeeded, performData = callbackResult.UpkeepNeeded, callbackResult.PerformData - // do tenderly simulations - rawCall, err := core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.extraData) + upkeepNeeded, performData = automationCheckResult.Eligible, automationCheckResult.PerformData + // do tenderly simulations for checkCallback + var rawCall []byte + rawCall, err = core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.ExtraData) if err != nil { failUnknown("failed to pack raw checkCallback call", err) } addLink("checkCallback simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) - rawCall, err = core.StreamsCompatibleABI.Pack("checkCallback", values, streamsLookup.extraData) + rawCall, err = core.StreamsCompatibleABI.Pack("checkCallback", values, streamsLookup.ExtraData) if err != nil { failUnknown("failed to pack raw checkCallback (direct) call", err) } @@ -316,6 +355,39 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } } +func mustAutomationCheckResult(upkeepID *big.Int, checkResult iregistry21.CheckUpkeep, trigger ocr2keepers.Trigger) ocr2keepers.CheckResult { + upkeepIdentifier := mustUpkeepIdentifier(upkeepID) + checkResult2 := ocr2keepers.CheckResult{ + Eligible: checkResult.UpkeepNeeded, + IneligibilityReason: checkResult.UpkeepFailureReason, + UpkeepID: upkeepIdentifier, + Trigger: trigger, + WorkID: core.UpkeepWorkID(upkeepIdentifier, trigger), + GasAllocated: 0, + PerformData: checkResult.PerformData, + FastGasWei: checkResult.FastGasWei, + LinkNative: checkResult.LinkNative, + } + + return checkResult2 +} + +type blockSubscriber struct { + ethClient *ethclient.Client +} + +func (bs *blockSubscriber) LatestBlock() *ocr2keepers.BlockKey { + header, err := bs.ethClient.HeaderByNumber(context.Background(), nil) + if err != nil { + return nil + } + + return &ocr2keepers.BlockKey{ + Number: ocr2keepers.BlockNumber(header.Number.Uint64()), + Hash: header.Hash(), + } +} + func logMatchesTriggerConfig(log *types.Log, config automation_utils_2_1.LogTriggerConfig) bool { if log.Topics[0] != config.Topic0 { return false @@ -353,9 +425,27 @@ func packTriggerData(log *types.Log, blockTime uint64) ([]byte, error) { return b, nil } -func mustUpkeepWorkID(upkeepID *big.Int, blockNum uint64, blockHash [32]byte, txHash [32]byte, logIndex int64) [32]byte { - // TODO - this is a copy of the code in core.UpkeepWorkID - // We should refactor that code to be more easily exported ex not rely on Trigger structs +func mustUpkeepWorkID(upkeepID *big.Int, trigger ocr2keepers.Trigger) [32]byte { + upkeepIdentifier := mustUpkeepIdentifier(upkeepID) + + workID := core.UpkeepWorkID(upkeepIdentifier, trigger) + workIDBytes, err := hex.DecodeString(workID) + if err != nil { + failUnknown("failed to decode workID", err) + } + + var result [32]byte + copy(result[:], workIDBytes[:]) + return result +} + +func mustUpkeepIdentifier(upkeepID *big.Int) ocr2keepers.UpkeepIdentifier { + upkeepIdentifier := &ocr2keepers.UpkeepIdentifier{} + upkeepIdentifier.FromBigInt(upkeepID) + return *upkeepIdentifier +} + +func mustAutomationTrigger(txHash [32]byte, logIndex int64, blockNum uint64, blockHash [32]byte) ocr2keepers.Trigger { trigger := ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ TxHash: txHash, @@ -364,16 +454,7 @@ func mustUpkeepWorkID(upkeepID *big.Int, blockNum uint64, blockHash [32]byte, tx BlockHash: blockHash, }, } - upkeepIdentifier := &ocr2keepers.UpkeepIdentifier{} - upkeepIdentifier.FromBigInt(upkeepID) - workID := core.UpkeepWorkID(*upkeepIdentifier, trigger) - workIDBytes, err := hex.DecodeString(workID) - if err != nil { - failUnknown("failed to decode workID", err) - } - var result [32]byte - copy(result[:], workIDBytes[:]) - return result + return trigger } func message(msg string) { @@ -385,11 +466,11 @@ func warning(msg string) { } func resolveIneligible(msg string) { - exit(fmt.Sprintf("✅ %s: this upkeep is not currently elligible", msg), nil, 0) + exit(fmt.Sprintf("✅ %s: this upkeep is not currently eligible", msg), nil, 0) } func resolveEligible() { - exit("❌ this upkeep is currently elligible", nil, 0) + exit("❌ this upkeep is currently eligible", nil, 0) } func rerun(msg string, err error) { @@ -490,5 +571,3 @@ func tenderlySimLink(cfg *config.Config, chainID int64, blockNumber uint64, inpu } return common.TenderlySimLink(responseJSON.Simulation.Id) } - -// TODO - link to performUpkeep tx if exists diff --git a/core/scripts/chaincli/handler/mercury_lookup_handler.go b/core/scripts/chaincli/handler/mercury_lookup_handler.go deleted file mode 100644 index 1165d83921d..00000000000 --- a/core/scripts/chaincli/handler/mercury_lookup_handler.go +++ /dev/null @@ -1,536 +0,0 @@ -package handler - -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "math/big" - "net/http" - "net/url" - "strconv" - "strings" - "time" - - "github.com/avast/retry-go" - ethabi "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rpc" - "github.com/pkg/errors" -) - -// MercuryLookupHandler is responsible for initiating the calls to the Mercury server -// to determine whether the upkeeps are eligible -type MercuryLookupHandler struct { - credentials *MercuryCredentials - httpClient HttpClient - rpcClient *rpc.Client -} - -func NewMercuryLookupHandler( - credentials *MercuryCredentials, - rpcClient *rpc.Client, -) *MercuryLookupHandler { - return &MercuryLookupHandler{ - credentials: credentials, - httpClient: http.DefaultClient, - rpcClient: rpcClient, - } -} - -type MercuryVersion string - -type StreamsLookup struct { - feedParamKey string - feeds []string - timeParamKey string - time *big.Int - extraData []byte - upkeepId *big.Int - block uint64 -} - -//go:generate mockery --quiet --name HttpClient --output ./mocks/ --case=underscore -type HttpClient interface { - Do(req *http.Request) (*http.Response, error) -} - -type MercuryCredentials struct { - LegacyURL string - URL string - ClientID string - ClientKey string -} - -func (mc *MercuryCredentials) Validate() bool { - return mc.URL != "" && mc.ClientID != "" && mc.ClientKey != "" -} - -type MercuryData struct { - Index int - Error error - Retryable bool - Bytes [][]byte - State PipelineExecutionState -} - -// MercuryV02Response represents a JSON structure used by Mercury v0.2 -type MercuryV02Response struct { - ChainlinkBlob string `json:"chainlinkBlob"` -} - -// MercuryV03Response represents a JSON structure used by Mercury v0.3 -type MercuryV03Response struct { - Reports []MercuryV03Report `json:"reports"` -} - -type MercuryV03Report struct { - FeedID string `json:"feedID"` // feed id in hex encoded - ValidFromTimestamp uint32 `json:"validFromTimestamp"` - ObservationsTimestamp uint32 `json:"observationsTimestamp"` - FullReport string `json:"fullReport"` // the actual hex encoded mercury report of this feed, can be sent to verifier -} - -const ( - // DefaultAllowListExpiration decides how long an upkeep's allow list info will be valid for. - DefaultAllowListExpiration = 20 * time.Minute - // CleanupInterval decides when the expired items in cache will be deleted. - CleanupInterval = 25 * time.Minute -) - -const ( - ApplicationJson = "application/json" - BlockNumber = "blockNumber" // valid for v0.2 - FeedIDs = "feedIDs" // valid for v0.3 - FeedIdHex = "feedIdHex" // valid for v0.2 - HeaderAuthorization = "Authorization" - HeaderContentType = "Content-Type" - HeaderTimestamp = "X-Authorization-Timestamp" - HeaderSignature = "X-Authorization-Signature-SHA256" - HeaderUpkeepId = "X-Authorization-Upkeep-Id" - MercuryPathV2 = "/client?" // only used to access mercury v0.2 server - MercuryBatchPathV3 = "/api/v1/reports/bulk?" // only used to access mercury v0.3 server - RetryDelay = 500 * time.Millisecond - Timestamp = "timestamp" // valid for v0.3 - TotalAttempt = 3 - UserId = "userId" -) - -type UpkeepFailureReason uint8 -type PipelineExecutionState uint8 - -const ( - // upkeep failure onchain reasons - UpkeepFailureReasonNone UpkeepFailureReason = 0 - UpkeepFailureReasonUpkeepCancelled UpkeepFailureReason = 1 - UpkeepFailureReasonUpkeepPaused UpkeepFailureReason = 2 - UpkeepFailureReasonTargetCheckReverted UpkeepFailureReason = 3 - UpkeepFailureReasonUpkeepNotNeeded UpkeepFailureReason = 4 - UpkeepFailureReasonPerformDataExceedsLimit UpkeepFailureReason = 5 - UpkeepFailureReasonInsufficientBalance UpkeepFailureReason = 6 - UpkeepFailureReasonMercuryCallbackReverted UpkeepFailureReason = 7 - UpkeepFailureReasonRevertDataExceedsLimit UpkeepFailureReason = 8 - UpkeepFailureReasonRegistryPaused UpkeepFailureReason = 9 - // leaving a gap here for more onchain failure reasons in the future - // upkeep failure offchain reasons - UpkeepFailureReasonMercuryAccessNotAllowed UpkeepFailureReason = 32 - UpkeepFailureReasonTxHashNoLongerExists UpkeepFailureReason = 33 - UpkeepFailureReasonInvalidRevertDataInput UpkeepFailureReason = 34 - UpkeepFailureReasonSimulationFailed UpkeepFailureReason = 35 - UpkeepFailureReasonTxHashReorged UpkeepFailureReason = 36 - - // pipeline execution error - NoPipelineError PipelineExecutionState = 0 - CheckBlockTooOld PipelineExecutionState = 1 - CheckBlockInvalid PipelineExecutionState = 2 - RpcFlakyFailure PipelineExecutionState = 3 - MercuryFlakyFailure PipelineExecutionState = 4 - PackUnpackDecodeFailed PipelineExecutionState = 5 - MercuryUnmarshalError PipelineExecutionState = 6 - InvalidMercuryRequest PipelineExecutionState = 7 - InvalidMercuryResponse PipelineExecutionState = 8 // this will only happen if Mercury server sends bad responses - UpkeepNotAuthorized PipelineExecutionState = 9 -) - -// UpkeepPrivilegeConfig represents the administrative offchain config for each upkeep. It can be set by s_upkeepPrivilegeManager -// role on the registry. Upkeeps allowed to use Mercury server will have this set to true. -type UpkeepPrivilegeConfig struct { - MercuryEnabled bool `json:"mercuryEnabled"` -} - -// generateHMAC calculates a user HMAC for Mercury server authentication. -func (mlh *MercuryLookupHandler) generateHMAC(method string, path string, body []byte, clientId string, secret string, ts int64) string { - bodyHash := sha256.New() - bodyHash.Write(body) - hashString := fmt.Sprintf("%s %s %s %s %d", - method, - path, - hex.EncodeToString(bodyHash.Sum(nil)), - clientId, - ts) - signedMessage := hmac.New(sha256.New, []byte(secret)) - signedMessage.Write([]byte(hashString)) - userHmac := hex.EncodeToString(signedMessage.Sum(nil)) - return userHmac -} - -// singleFeedRequest sends a v0.2 Mercury request for a single feed report. -func (mlh *MercuryLookupHandler) singleFeedRequest(ctx context.Context, ch chan<- MercuryData, index int, ml *StreamsLookup) { - q := url.Values{ - ml.feedParamKey: {ml.feeds[index]}, - ml.timeParamKey: {ml.time.String()}, - } - mercuryURL := mlh.credentials.LegacyURL - reqUrl := fmt.Sprintf("%s%s%s", mercuryURL, MercuryPathV2, q.Encode()) - // mlh.logger.Debugf("request URL for upkeep %s feed %s: %s", ml.upkeepId.String(), ml.feeds[index], reqUrl) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) - if err != nil { - ch <- MercuryData{Index: index, Error: err, Retryable: false, State: InvalidMercuryRequest} - return - } - - ts := time.Now().UTC().UnixMilli() - signature := mlh.generateHMAC(http.MethodGet, MercuryPathV2+q.Encode(), []byte{}, mlh.credentials.ClientID, mlh.credentials.ClientKey, ts) - req.Header.Set(HeaderContentType, ApplicationJson) - req.Header.Set(HeaderAuthorization, mlh.credentials.ClientID) - req.Header.Set(HeaderTimestamp, strconv.FormatInt(ts, 10)) - req.Header.Set(HeaderSignature, signature) - - // in the case of multiple retries here, use the last attempt's data - state := NoPipelineError - retryable := false - sent := false - retryErr := retry.Do( - func() error { - retryable = false - resp, err1 := mlh.httpClient.Do(req) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup GET request failed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "feed", ml.feeds[index], "error", err1) - retryable = true - state = MercuryFlakyFailure - return err1 - } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - _ = "" // placate linter - // mlh.logger.Errorf("Encountered error when closing the body of the response in single feed: %s", err) - } - }(resp.Body) - - body, err1 := io.ReadAll(resp.Body) - if err1 != nil { - retryable = false - state = InvalidMercuryResponse - return err1 - } - - if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError { - // mlh.logger.Errorw("StreamsLookup received retryable status code", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "statusCode", resp.StatusCode, "feed", ml.feeds[index]) - retryable = true - state = MercuryFlakyFailure - return errors.New(strconv.FormatInt(int64(resp.StatusCode), 10)) - } else if resp.StatusCode != http.StatusOK { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("StreamsLookup upkeep %s block %s received status code %d for feed %s", ml.upkeepId.String(), ml.time.String(), resp.StatusCode, ml.feeds[index]) - } - - // mlh.logger.Debugf("at block %s upkeep %s received status code %d from mercury v0.2 with BODY=%s", ml.time.String(), ml.upkeepId.String(), resp.StatusCode, hexutil.Encode(body)) - - var m MercuryV02Response - err1 = json.Unmarshal(body, &m) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to unmarshal body to MercuryResponse", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "feed", ml.feeds[index], "error", err1) - retryable = false - state = MercuryUnmarshalError - return err1 - } - blobBytes, err1 := hexutil.Decode(m.ChainlinkBlob) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to decode chainlinkBlob for feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "blob", m.ChainlinkBlob, "feed", ml.feeds[index], "error", err1) - retryable = false - state = InvalidMercuryResponse - return err1 - } - ch <- MercuryData{ - Index: index, - Bytes: [][]byte{blobBytes}, - Retryable: false, - State: NoPipelineError, - } - sent = true - return nil - }, - // only retry when the error is 404 Not Found or 500 Internal Server Error - retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) - }), - retry.Context(ctx), - retry.Delay(RetryDelay), - retry.Attempts(TotalAttempt)) - - if !sent { - md := MercuryData{ - Index: index, - Bytes: [][]byte{}, - Retryable: retryable, - Error: fmt.Errorf("failed to request feed for %s: %w", ml.feeds[index], retryErr), - State: state, - } - ch <- md - } -} - -// multiFeedsRequest sends a Mercury v0.3 request for a multi-feed report -func (mlh *MercuryLookupHandler) multiFeedsRequest(ctx context.Context, ch chan<- MercuryData, ml *StreamsLookup) { - params := fmt.Sprintf("%s=%s&%s=%s", FeedIDs, strings.Join(ml.feeds, ","), Timestamp, ml.time.String()) - reqUrl := fmt.Sprintf("%s%s%s", mlh.credentials.URL, MercuryBatchPathV3, params) - // mlh.logger.Debugf("request URL for upkeep %s userId %s: %s", ml.upkeepId.String(), mlh.credentials.ClientID, reqUrl) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) - if err != nil { - ch <- MercuryData{Index: 0, Error: err, Retryable: false, State: InvalidMercuryRequest} - return - } - - ts := time.Now().UTC().UnixMilli() - signature := mlh.generateHMAC(http.MethodGet, MercuryBatchPathV3+params, []byte{}, mlh.credentials.ClientID, mlh.credentials.ClientKey, ts) - req.Header.Set(HeaderContentType, ApplicationJson) - // username here is often referred to as user id - req.Header.Set(HeaderAuthorization, mlh.credentials.ClientID) - req.Header.Set(HeaderTimestamp, strconv.FormatInt(ts, 10)) - req.Header.Set(HeaderSignature, signature) - // mercury will inspect authorization headers above to make sure this user (in automation's context, this node) is eligible to access mercury - // and if it has an automation role. it will then look at this upkeep id to check if it has access to all the requested feeds. - req.Header.Set(HeaderUpkeepId, ml.upkeepId.String()) - - // in the case of multiple retries here, use the last attempt's data - state := NoPipelineError - retryable := false - sent := false - retryErr := retry.Do( - func() error { - retryable = false - resp, err1 := mlh.httpClient.Do(req) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup GET request fails for multi feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "error", err1) - retryable = true - state = MercuryFlakyFailure - return err1 - } - defer func(Body io.ReadCloser) { - err := Body.Close() - if err != nil { - _ = "" // placate linter - // mlh.logger.Errorf("Encountered error when closing the body of the response in the multi feed: %s", err) - } - }(resp.Body) - body, err1 := io.ReadAll(resp.Body) - if err1 != nil { - retryable = false - state = InvalidMercuryResponse - return err1 - } - - // mlh.logger.Infof("at timestamp %s upkeep %s received status code %d from mercury v0.3", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - if resp.StatusCode == http.StatusUnauthorized { - retryable = false - state = UpkeepNotAuthorized - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by unauthorized upkeep", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode == http.StatusBadRequest { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by invalid format of timestamp", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode == http.StatusInternalServerError { - retryable = true - state = MercuryFlakyFailure - return fmt.Errorf("%d", http.StatusInternalServerError) - } else if resp.StatusCode == 420 { - // in 0.3, this will happen when missing/malformed query args, missing or bad required headers, non-existent feeds, or no permissions for feeds - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3, most likely this is caused by missing/malformed query args, missing or bad required headers, non-existent feeds, or no permissions for feeds", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode != http.StatusOK { - retryable = false - state = InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3", ml.time.String(), ml.upkeepId.String(), resp.StatusCode) - } - - var response MercuryV03Response - err1 = json.Unmarshal(body, &response) - if err1 != nil { - // mlh.logger.Errorw("StreamsLookup failed to unmarshal body to MercuryResponse for multi feed", "upkeepID", ml.upkeepId.String(), "time", ml.time.String(), "error", err1) - retryable = false - state = MercuryUnmarshalError - return err1 - } - // in v0.3, if some feeds are not available, the server will only return available feeds, but we need to make sure ALL feeds are retrieved before calling user contract - // hence, retry in this case. retry will help when we send a very new timestamp and reports are not yet generated - if len(response.Reports) != len(ml.feeds) { - // TODO: AUTO-5044: calculate what reports are missing and log a warning - retryable = true - state = MercuryFlakyFailure - return fmt.Errorf("%d", http.StatusNotFound) - } - var reportBytes [][]byte - for _, rsp := range response.Reports { - b, err := hexutil.Decode(rsp.FullReport) - if err != nil { - retryable = false - state = InvalidMercuryResponse - return err - } - reportBytes = append(reportBytes, b) - } - ch <- MercuryData{ - Index: 0, - Bytes: reportBytes, - Retryable: false, - State: NoPipelineError, - } - sent = true - return nil - }, - // only retry when the error is 404 Not Found or 500 Internal Server Error - retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) - }), - retry.Context(ctx), - retry.Delay(RetryDelay), - retry.Attempts(TotalAttempt)) - - if !sent { - md := MercuryData{ - Index: 0, - Bytes: [][]byte{}, - Retryable: retryable, - Error: retryErr, - State: state, - } - ch <- md - } -} - -// doMercuryRequest sends requests to Mercury API to retrieve ChainlinkBlob. -func (mlh *MercuryLookupHandler) doMercuryRequest(ctx context.Context, ml *StreamsLookup) (PipelineExecutionState, UpkeepFailureReason, [][]byte, bool, error) { - var isMercuryV03 bool - resultLen := len(ml.feeds) - ch := make(chan MercuryData, resultLen) - if len(ml.feeds) == 0 { - return NoPipelineError, UpkeepFailureReasonInvalidRevertDataInput, nil, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", ml.feedParamKey, ml.timeParamKey, ml.feeds) - } - if ml.feedParamKey == FeedIdHex && ml.timeParamKey == BlockNumber { - // only v0.2 - for i := range ml.feeds { - go mlh.singleFeedRequest(ctx, ch, i, ml) - } - } else if ml.feedParamKey == FeedIDs && ml.timeParamKey == Timestamp { - // only v0.3 - resultLen = 1 - isMercuryV03 = true - ch = make(chan MercuryData, resultLen) - go mlh.multiFeedsRequest(ctx, ch, ml) - } else { - return NoPipelineError, UpkeepFailureReasonInvalidRevertDataInput, nil, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", ml.feedParamKey, ml.timeParamKey, ml.feeds) - } - - var reqErr error - results := make([][]byte, len(ml.feeds)) - retryable := true - allSuccess := true - // in v0.2, use the last execution error as the state, if no execution errors, state will be no error - state := NoPipelineError - for i := 0; i < resultLen; i++ { - m := <-ch - if m.Error != nil { - if reqErr == nil { - reqErr = errors.New(m.Error.Error()) - } else { - reqErr = errors.New(reqErr.Error() + m.Error.Error()) - } - retryable = retryable && m.Retryable - allSuccess = false - if m.State != NoPipelineError { - state = m.State - } - continue - } - if isMercuryV03 { - results = m.Bytes - } else { - results[m.Index] = m.Bytes[0] - } - } - // only retry when not all successful AND none are not retryable - return state, UpkeepFailureReasonNone, results, retryable && !allSuccess, reqErr -} - -// decodeStreamsLookup decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string timeParamKey, uint256 time, byte[] extraData) -// func (mlh *MercuryLookupHandler) decodeStreamsLookup(data []byte) (*StreamsLookup, error) { -// e := mlh.mercuryConfig.Abi.Errors["StreamsLookup"] -// unpack, err := e.Unpack(data) -// if err != nil { -// return nil, fmt.Errorf("unpack error: %w", err) -// } -// errorParameters := unpack.([]interface{}) - -// return &StreamsLookup{ -// feedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), -// feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), -// timeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), -// time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), -// extraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), -// }, nil -// } - -// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if -// this upkeep is allowed to use Mercury service. -// func (mlh *MercuryLookupHandler) allowedToUseMercury(upkeep models.Upkeep) (bool, error) { -// allowed, ok := mlh.mercuryConfig.AllowListCache.Get(upkeep.Admin.Hex()) -// if ok { -// return allowed.(bool), nil -// } - -// if upkeep.UpkeepPrivilegeConfig == nil { -// return false, fmt.Errorf("the upkeep privilege config was not retrieved for upkeep with ID %s", upkeep.UpkeepID) -// } - -// if len(upkeep.UpkeepPrivilegeConfig) == 0 { -// return false, fmt.Errorf("the upkeep privilege config is empty") -// } - -// var a UpkeepPrivilegeConfig -// err := json.Unmarshal(upkeep.UpkeepPrivilegeConfig, &a) -// if err != nil { -// return false, fmt.Errorf("failed to unmarshal privilege config for upkeep ID %s: %v", upkeep.UpkeepID, err) -// } - -// mlh.mercuryConfig.AllowListCache.Set(upkeep.Admin.Hex(), a.MercuryEnabled, cache.DefaultExpiration) -// return a.MercuryEnabled, nil -// } - -func (mlh *MercuryLookupHandler) CheckCallback(ctx context.Context, values [][]byte, lookup *StreamsLookup, registryABI ethabi.ABI, registryAddress common.Address) (hexutil.Bytes, error) { - payload, err := registryABI.Pack("checkCallback", lookup.upkeepId, values, lookup.extraData) - if err != nil { - return nil, err - } - - var theBytes hexutil.Bytes - args := map[string]interface{}{ - "to": registryAddress.Hex(), - "data": hexutil.Bytes(payload), - } - - // call checkCallback function at the block which OCR3 has agreed upon - err = mlh.rpcClient.CallContext(ctx, &theBytes, "eth_call", args, hexutil.EncodeUint64(lookup.block)) - if err != nil { - return nil, err - } - return theBytes, nil -} diff --git a/core/scripts/go.mod b/core/scripts/go.mod index db71535723e..895363a53f8 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -6,7 +6,6 @@ go 1.21.3 replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( - github.com/avast/retry-go v3.0.0+incompatible github.com/docker/docker v24.0.7+incompatible github.com/docker/go-connections v0.4.0 github.com/ethereum/go-ethereum v1.12.0 @@ -18,7 +17,6 @@ require ( github.com/montanaflynn/stats v0.7.1 github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml/v2 v2.1.1 - github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 @@ -217,6 +215,7 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/pressly/goose/v3 v3.16.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 1c48074ea3a..dcf47d392cc 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -145,8 +145,6 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= -github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index 70db40c26bb..d64a9e7ef83 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -16,7 +16,6 @@ import ( "github.com/patrickmn/go-cache" ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink-common/pkg/services" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" @@ -131,7 +130,6 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper // tried to call mercury lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResults[i].PerformData)) streamsLookupErr, err := s.packer.DecodeStreamsLookupRequest(checkResult.PerformData) - if err != nil { lookupLggr.Debugf("at block %d upkeep %s DecodeStreamsLookupRequest failed: %v", block, upkeepId, err) // user contract did not revert with StreamsLookup error @@ -149,7 +147,7 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper if streamsLookupResponse.IsMercuryV02() { // check permission on the registry for mercury v0.2 opts := s.buildCallOpts(ctx, block) - if state, reason, retryable, allowed, err := s.allowedToUseMercury(opts, upkeepId.BigInt()); err != nil { + if state, reason, retryable, allowed, err := s.AllowedToUseMercury(opts, upkeepId.BigInt()); err != nil { lookupLggr.Warnf("at block %s upkeep %s failed to query mercury allow list: %s", block, upkeepId, err) checkResults[i].PipelineExecutionState = uint8(state) checkResults[i].IneligibilityReason = uint8(reason) @@ -178,65 +176,93 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *mercury.StreamsLookup, i int, checkResults []ocr2keepers.CheckResult) { defer wg.Done() - state, reason, values, retryable, retryInterval, err := mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0*time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", lookup.FeedParamKey, lookup.TimeParamKey, lookup.Feeds) - pluginRetryKey := generatePluginRetryKey(checkResults[i].WorkID, lookup.Block) + values, err := s.DoMercuryRequest(ctx, lookup, checkResults, i) + if err != nil { + s.lggr.Errorf("at block %d upkeep %s requested time %s DoMercuryRequest err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) + } - if lookup.IsMercuryV02() { - state, reason, values, retryable, retryInterval, err = s.v02Client.DoRequest(ctx, lookup, pluginRetryKey) - } else if lookup.IsMercuryV03() { - state, reason, values, retryable, retryInterval, err = s.v03Client.DoRequest(ctx, lookup, pluginRetryKey) + if err := s.CheckCallback(ctx, values, lookup, checkResults, i); err != nil { + s.lggr.Errorf("at block %d upkeep %s requested time %s CheckCallback err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) } +} +func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *mercury.StreamsLookup, checkResults []ocr2keepers.CheckResult, i int) error { + payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) if err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s retryable %v retryInterval %s doMercuryRequest: %s", lookup.Block, lookup.UpkeepId, lookup.Time, retryable, retryInterval, err.Error()) - checkResults[i].Retryable = retryable - checkResults[i].RetryInterval = retryInterval - checkResults[i].PipelineExecutionState = uint8(state) - checkResults[i].IneligibilityReason = uint8(reason) - return + checkResults[i].Retryable = false + checkResults[i].PipelineExecutionState = uint8(mercury.PackUnpackDecodeFailed) + return err } - for j, v := range values { - s.lggr.Infof("at block %d upkeep %s requested time %s doMercuryRequest values[%d]: %s", lookup.Block, lookup.UpkeepId, lookup.Time, j, hexutil.Encode(v)) + var mercuryBytes hexutil.Bytes + args := map[string]interface{}{ + "to": s.registry.Address().Hex(), + "data": hexutil.Bytes(payload), } - state, retryable, mercuryBytes, err := s.checkCallback(ctx, values, lookup) - if err != nil { - s.lggr.Errorf("at block %d upkeep %s checkCallback err: %s", lookup.Block, lookup.UpkeepId, err.Error()) - checkResults[i].Retryable = retryable - checkResults[i].PipelineExecutionState = uint8(state) - return + // call checkCallback function at the block which OCR3 has agreed upon + if err = s.client.CallContext(ctx, &mercuryBytes, "eth_call", args, hexutil.EncodeUint64(lookup.Block)); err != nil { + checkResults[i].Retryable = true + checkResults[i].PipelineExecutionState = uint8(mercury.RpcFlakyFailure) + return err } + s.lggr.Infof("at block %d upkeep %s requested time %s checkCallback mercuryBytes: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(mercuryBytes)) unpackCallBackState, needed, performData, failureReason, _, err := s.packer.UnpackCheckCallbackResult(mercuryBytes) if err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s UnpackCheckCallbackResult err: %s", lookup.Block, lookup.UpkeepId, lookup.Time, err.Error()) checkResults[i].PipelineExecutionState = unpackCallBackState - return + return err } if failureReason == uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) { checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) s.lggr.Debugf("at block %d upkeep %s requested time %s mercury callback reverts", lookup.Block, lookup.UpkeepId, lookup.Time) - return + return nil + } if !needed { checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonUpkeepNotNeeded) s.lggr.Debugf("at block %d upkeep %s requested time %s callback reports upkeep not needed", lookup.Block, lookup.UpkeepId, lookup.Time) - return + return nil } checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) checkResults[i].Eligible = true checkResults[i].PerformData = performData s.lggr.Infof("at block %d upkeep %s requested time %s successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) + + return nil } -// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if +func (s *streams) DoMercuryRequest(ctx context.Context, lookup *mercury.StreamsLookup, checkResults []ocr2keepers.CheckResult, i int) ([][]byte, error) { + state, reason, values, retryable, retryInterval, err := mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0*time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", lookup.FeedParamKey, lookup.TimeParamKey, lookup.Feeds) + pluginRetryKey := generatePluginRetryKey(checkResults[i].WorkID, lookup.Block) + + if lookup.IsMercuryV02() { + state, reason, values, retryable, retryInterval, err = s.v02Client.DoRequest(ctx, lookup, pluginRetryKey) + } else if lookup.IsMercuryV03() { + state, reason, values, retryable, retryInterval, err = s.v03Client.DoRequest(ctx, lookup, pluginRetryKey) + } + + if err != nil { + checkResults[i].Retryable = retryable + checkResults[i].RetryInterval = retryInterval + checkResults[i].PipelineExecutionState = uint8(state) + checkResults[i].IneligibilityReason = uint8(reason) + return nil, err + } + + for j, v := range values { + s.lggr.Infof("at block %d upkeep %s requested time %s doMercuryRequest values[%d]: %s", lookup.Block, lookup.UpkeepId, lookup.Time, j, hexutil.Encode(v)) + } + return values, nil +} + +// AllowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if // this upkeep is allowed to use Mercury service. -func (s *streams) allowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state mercury.MercuryUpkeepState, reason mercury.MercuryUpkeepFailureReason, retryable bool, allow bool, err error) { +func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state mercury.MercuryUpkeepState, reason mercury.MercuryUpkeepFailureReason, retryable bool, allow bool, err error) { allowed, ok := s.mercuryConfig.IsUpkeepAllowed(upkeepId.String()) if ok { return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, allowed.(bool), nil @@ -256,7 +282,6 @@ func (s *streams) allowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s "data": hexutil.Bytes(payload), } - // call checkCallback function at the block which OCR3 has agreed upon if err = s.client.CallContext(opts.Context, &resultBytes, "eth_call", args, hexutil.EncodeBig(opts.BlockNumber)); err != nil { return mercury.RpcFlakyFailure, mercury.MercuryUpkeepFailureReasonNone, true, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } @@ -282,26 +307,6 @@ func (s *streams) allowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, privilegeConfig.MercuryEnabled, nil } -func (s *streams) checkCallback(ctx context.Context, values [][]byte, lookup *mercury.StreamsLookup) (mercury.MercuryUpkeepState, bool, hexutil.Bytes, error) { - payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) - if err != nil { - return mercury.PackUnpackDecodeFailed, false, nil, err - } - - var b hexutil.Bytes - args := map[string]interface{}{ - "to": s.registry.Address().Hex(), - "data": hexutil.Bytes(payload), - } - - // call checkCallback function at the block which OCR3 has agreed upon - if err := s.client.CallContext(ctx, &b, "eth_call", args, hexutil.EncodeUint64(lookup.Block)); err != nil { - return mercury.RpcFlakyFailure, true, nil, err - } - - return mercury.NoPipelineError, false, b, nil -} - func (s *streams) buildCallOpts(ctx context.Context, block *big.Int) *bind.CallOpts { opts := bind.CallOpts{ Context: ctx, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index 32041d0f18b..caa3efb974b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -127,6 +127,7 @@ func TestStreams_CheckCallback(t *testing.T) { tests := []struct { name string lookup *mercury.StreamsLookup + input []ocr2keepers.CheckResult values [][]byte statusCode int @@ -154,6 +155,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 48, 120, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -186,6 +190,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -217,6 +224,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{}, @@ -256,10 +266,10 @@ func TestStreams_CheckCallback(t *testing.T) { }).Once() s.client = client - state, retryable, _, err := s.checkCallback(testutils.Context(t), tt.values, tt.lookup) - tt.wantErr(t, err, fmt.Sprintf("Error asserion failed: %v", tt.name)) - assert.Equal(t, tt.state, state) - assert.Equal(t, tt.retryable, retryable) + err = s.CheckCallback(testutils.Context(t), tt.values, tt.lookup, tt.input, 0) + tt.wantErr(t, err, fmt.Sprintf("Error assertion failed: %v", tt.name)) + assert.Equal(t, uint8(tt.state), tt.input[0].PipelineExecutionState) + assert.Equal(t, tt.retryable, tt.input[0].Retryable) }) } } @@ -435,7 +445,7 @@ func TestStreams_AllowedToUseMercury(t *testing.T) { BlockNumber: big.NewInt(10), } - state, reason, retryable, allowed, err := s.allowedToUseMercury(opts, upkeepId) + state, reason, retryable, allowed, err := s.AllowedToUseMercury(opts, upkeepId) assert.Equal(t, tt.err, err) assert.Equal(t, tt.allowed, allowed) assert.Equal(t, tt.state, state) From ee2996fa748812dc406fd6147d54f2c1e53a4ade Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 13 Dec 2023 16:26:36 -0500 Subject: [PATCH 03/15] Adds MAKE Command for Building Plugin Image (#11567) --- integration-tests/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integration-tests/Makefile b/integration-tests/Makefile index fb4bfa74f3e..86a266a996a 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -192,6 +192,12 @@ test_reorg_automation: ## Run the automation reorg tests build_docker_image: docker build -f ../core/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t $(image):$(tag) ../ +# image: the name for the chainlink image being built, example: image=chainlink +# tag: the tag for the chainlink image being built, example: tag=latest +# example usage: make build_docker_image image=chainlink tag=latest +.PHONY: build_plugin_docker_image +build_docker_image: + docker build -f ../plugins/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t localhost:5000/chainlink:develop ../ # image: the name for the chainlink image being built, example: image=chainlink # tag: the tag for the chainlink image being built, example: tag=latest From 8b2c48dbcdd31ed5defff930f66b8cc7a49b9817 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 13 Dec 2023 14:42:33 -0800 Subject: [PATCH 04/15] Mercury Packer (#11521) * Refactor encoding.Packer to have Mercury control its own interface and function implementations * rebase and update --- core/scripts/chaincli/handler/debug.go | 37 ++-- .../evmregistry/v21/encoding/interface.go | 7 - .../evmregistry/v21/encoding/packer.go | 48 ----- .../evmregistry/v21/encoding/packer_test.go | 186 ----------------- .../evmregistry/v21/mercury/mercury.go | 75 ++++++- .../evmregistry/v21/mercury/mercury_test.go | 193 ++++++++++++++++++ .../v21/mercury/streams/streams.go | 6 +- .../v21/mercury/streams/streams_test.go | 2 - .../ocr2keeper/evmregistry/v21/registry.go | 3 +- 9 files changed, 290 insertions(+), 267 deletions(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 815a0c9a030..3e8bd6d597e 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -23,7 +23,6 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" @@ -33,6 +32,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" @@ -44,6 +44,7 @@ const ( expectedTypeAndVersion = "KeeperRegistry 2.1.0" ) +var mercuryPacker = mercury.NewAbiPacker() var packer = encoding.NewAbiPacker() var links []string @@ -258,10 +259,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) lggr, _ := logger.NewLogger() blockSub := &blockSubscriber{k.client} - streams := streams.NewStreamsLookup(packer, mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) + streams := streams.NewStreamsLookup(mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) var streamsLookupErr *mercury.StreamsLookupError - streamsLookupErr, err = packer.DecodeStreamsLookupRequest(checkResult.PerformData) + streamsLookupErr, err = mercuryPacker.DecodeStreamsLookupRequest(checkResult.PerformData) if err == nil { message("upkeep reverted with StreamsLookup") message(fmt.Sprintf("StreamsLookup data: {FeedParamKey: %s, Feeds: %v, TimeParamKey: %s, Time: %d, ExtraData: %s}", streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time.Uint64(), hexutil.Encode(streamsLookupErr.ExtraData))) @@ -282,7 +283,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { message("using mercury lookup v0.2") // check if upkeep is allowed to use mercury v0.2 var allowed bool - _, _, _, allowed, err = streams.AllowedToUseMercury(latestCallOpts, upkeepID) + _, _, _, allowed, err = streams.AllowedToUseMercury(triggerCallOpts, upkeepID) if err != nil { failUnknown("failed to check if upkeep is allowed to use mercury", err) } @@ -307,10 +308,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { var values [][]byte values, err = streams.DoMercuryRequest(ctx, streamsLookup, checkResults, 0) - if automationCheckResult.IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { + if checkResults[0].IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { resolveIneligible("upkeep used invalid revert data") } - if automationCheckResult.PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { + if checkResults[0].PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { resolveIneligible("the mercury request data is invalid") } if err != nil { @@ -322,10 +323,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failUnknown("failed to execute mercury callback ", err) } - if automationCheckResult.IneligibilityReason != 0 { - message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", automationCheckResult.IneligibilityReason)) + if checkResults[0].IneligibilityReason != 0 { + message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResults[0].IneligibilityReason)) } - upkeepNeeded, performData = automationCheckResult.Eligible, automationCheckResult.PerformData + upkeepNeeded, performData = checkResults[0].Eligible, checkResults[0].PerformData // do tenderly simulations for checkCallback var rawCall []byte rawCall, err = core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.ExtraData) @@ -345,13 +346,23 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if !upkeepNeeded { resolveIneligible("upkeep is not needed") } - // simulate perform ukeep - simulateResult, err := keeperRegistry21.SimulatePerformUpkeep(latestCallOpts, upkeepID, performData) + // simulate perform upkeep + simulateResult, err := keeperRegistry21.SimulatePerformUpkeep(triggerCallOpts, upkeepID, performData) if err != nil { failUnknown("failed to simulate perform upkeep: ", err) } + + // do tenderly simulation + rawCall, err := core.RegistryABI.Pack("simulatePerformUpkeep", upkeepID, performData) + if err != nil { + failUnknown("failed to pack raw simulatePerformUpkeep call", err) + } + addLink("simulatePerformUpkeep simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) + if simulateResult.Success { resolveEligible() + } else { + resolveIneligible("simulate perform upkeep unsuccessful") } } @@ -466,11 +477,11 @@ func warning(msg string) { } func resolveIneligible(msg string) { - exit(fmt.Sprintf("✅ %s: this upkeep is not currently eligible", msg), nil, 0) + exit(fmt.Sprintf("❌ this upkeep is not eligible: %s", msg), nil, 0) } func resolveEligible() { - exit("❌ this upkeep is currently eligible", nil, 0) + exit("✅ this upkeep is eligible", nil, 0) } func rerun(msg string, err error) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go index 06a3e7b106b..ed3218ea405 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go @@ -1,13 +1,10 @@ package encoding import ( - "math/big" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) const ( @@ -51,12 +48,8 @@ type UpkeepInfo = iregistry21.KeeperRegistryBase21UpkeepInfo type Packer interface { UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw string) (ocr2keepers.CheckResult, error) - UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) UnpackPerformResult(raw string) (uint8, bool, error) UnpackLogTriggerConfig(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) PackReport(report automation_utils_2_1.KeeperRegistryBase21Report) ([]byte, error) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistryBase21Report, error) - PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) - UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) - DecodeStreamsLookupRequest(data []byte) (*mercury.StreamsLookupError, error) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go index 57013a6277a..4a92b4a17ec 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go @@ -11,7 +11,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) // triggerWrapper is a wrapper for the different trigger types (log and condition triggers). @@ -67,35 +66,6 @@ func (p *abiPacker) UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw str return result, nil } -func (p *abiPacker) PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { - return p.registryABI.Pack("getUpkeepPrivilegeConfig", upkeepId) -} - -func (p *abiPacker) UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) { - out, err := p.registryABI.Methods["getUpkeepPrivilegeConfig"].Outputs.UnpackValues(resp) - if err != nil { - return nil, fmt.Errorf("%w: unpack getUpkeepPrivilegeConfig return", err) - } - - bts := *abi.ConvertType(out[0], new([]byte)).(*[]byte) - - return bts, nil -} - -func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) { - out, err := p.registryABI.Methods["checkCallback"].Outputs.UnpackValues(callbackResp) - if err != nil { - return PackUnpackDecodeFailed, false, nil, 0, nil, fmt.Errorf("%w: unpack checkUpkeep return: %s", err, hexutil.Encode(callbackResp)) - } - - upkeepNeeded := *abi.ConvertType(out[0], new(bool)).(*bool) - rawPerformData := *abi.ConvertType(out[1], new([]byte)).(*[]byte) - failureReason := *abi.ConvertType(out[2], new(uint8)).(*uint8) - gasUsed := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) - - return NoPipelineError, upkeepNeeded, rawPerformData, failureReason, gasUsed, nil -} - func (p *abiPacker) UnpackPerformResult(raw string) (uint8, bool, error) { b, err := hexutil.Decode(raw) if err != nil { @@ -163,24 +133,6 @@ func (p *abiPacker) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistr return report, nil } -// DecodeStreamsLookupRequest decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string feedParamKey, uint256 time, byte[] extraData) -func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*mercury.StreamsLookupError, error) { - e := p.streamsABI.Errors["StreamsLookup"] - unpack, err := e.Unpack(data) - if err != nil { - return nil, fmt.Errorf("unpack error: %w", err) - } - errorParameters := unpack.([]interface{}) - - return &mercury.StreamsLookupError{ - FeedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), - Feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), - TimeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), - Time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), - ExtraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), - }, nil -} - // GetIneligibleCheckResultWithoutPerformData returns an ineligible check result with ineligibility reason and pipeline execution state but without perform data func GetIneligibleCheckResultWithoutPerformData(p ocr2keepers.UpkeepPayload, reason uint8, state uint8, retryable bool) ocr2keepers.CheckResult { return ocr2keepers.CheckResult{ diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go index 42fcd40d618..71c2755f150 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go @@ -1,8 +1,6 @@ package encoding import ( - "encoding/json" - "errors" "fmt" "math/big" "testing" @@ -17,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" automation21Utils "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) func TestPacker_PackReport(t *testing.T) { @@ -231,62 +228,6 @@ func TestPacker_UnpackPerformResult(t *testing.T) { } } -func TestPacker_UnpackCheckCallbackResult(t *testing.T) { - tests := []struct { - Name string - CallbackResp []byte - UpkeepNeeded bool - PerformData []byte - FailureReason uint8 - GasUsed *big.Int - ErrorString string - State uint8 - }{ - { - Name: "unpack upkeep needed", - CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: true, - PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - FailureReason: uint8(UpkeepFailureReasonNone), - GasUsed: big.NewInt(11796), - }, - { - Name: "unpack upkeep not needed", - CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: false, - PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - FailureReason: uint8(UpkeepFailureReasonUpkeepNotNeeded), - GasUsed: big.NewInt(13008), - }, - { - Name: "unpack malformed data", - CallbackResp: []byte{0, 0, 0, 23, 4, 163, 66, 91, 228, 102, 200, 84, 144, 233, 218, 44, 168, 192, 191, 253, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: false, - PerformData: nil, - ErrorString: "abi: improperly encoded boolean value: unpack checkUpkeep return: ", - State: PackUnpackDecodeFailed, - }, - } - for _, test := range tests { - t.Run(test.Name, func(t *testing.T) { - packer := NewAbiPacker() - - state, needed, pd, failureReason, gasUsed, err := packer.UnpackCheckCallbackResult(test.CallbackResp) - - if test.ErrorString != "" { - assert.EqualError(t, err, test.ErrorString+hexutil.Encode(test.CallbackResp)) - } else { - assert.Nil(t, err) - } - assert.Equal(t, test.UpkeepNeeded, needed) - assert.Equal(t, test.PerformData, pd) - assert.Equal(t, test.FailureReason, failureReason) - assert.Equal(t, test.GasUsed, gasUsed) - assert.Equal(t, test.State, state) - }) - } -} - func TestPacker_UnpackLogTriggerConfig(t *testing.T) { tests := []struct { name string @@ -349,130 +290,3 @@ func TestPacker_PackReport_UnpackReport(t *testing.T) { expected := "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000" assert.Equal(t, hexutil.Encode(res), expected) } - -func TestPacker_PackGetUpkeepPrivilegeConfig(t *testing.T) { - tests := []struct { - name string - upkeepId *big.Int - raw []byte - errored bool - }{ - { - name: "happy path", - upkeepId: func() *big.Int { - id, _ := new(big.Int).SetString("52236098515066839510538748191966098678939830769967377496848891145101407612976", 10) - - return id - }(), - raw: func() []byte { - b, _ := hexutil.Decode("0x19d97a94737c9583000000000000000000000001ea8ed6d0617dd5b3b87374020efaf030") - - return b - }(), - errored: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - packer := NewAbiPacker() - - b, err := packer.PackGetUpkeepPrivilegeConfig(test.upkeepId) - - if !test.errored { - require.NoError(t, err, "no error expected from packing") - - assert.Equal(t, test.raw, b, "raw bytes for output should match expected") - } else { - assert.NotNil(t, err, "error expected from packing function") - } - }) - } -} - -func TestPacker_UnpackGetUpkeepPrivilegeConfig(t *testing.T) { - tests := []struct { - name string - raw []byte - errored bool - }{ - { - name: "happy path", - raw: func() []byte { - b, _ := hexutil.Decode("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000177b226d657263757279456e61626c6564223a747275657d000000000000000000") - - return b - }(), - errored: false, - }, - { - name: "error empty config", - raw: func() []byte { - b, _ := hexutil.Decode("0x") - - return b - }(), - errored: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - packer := NewAbiPacker() - - b, err := packer.UnpackGetUpkeepPrivilegeConfig(test.raw) - - if !test.errored { - require.NoError(t, err, "should unpack bytes from abi encoded value") - - // the actual struct to unmarshal into is not available to this - // package so basic json encoding is the limit of the following test - var data map[string]interface{} - err = json.Unmarshal(b, &data) - - assert.NoError(t, err, "packed data should unmarshal using json encoding") - assert.Equal(t, []byte(`{"mercuryEnabled":true}`), b) - } else { - assert.NotNil(t, err, "error expected from unpack function") - } - }) - } -} - -func TestPacker_DecodeStreamsLookupRequest(t *testing.T) { - tests := []struct { - name string - data []byte - expected *mercury.StreamsLookupError - state uint8 - err error - }{ - { - name: "success - decode to streams lookup", - data: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000002435eb50000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), - expected: &mercury.StreamsLookupError{ - FeedParamKey: "feedIdHex", - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: "blockNumber", - Time: big.NewInt(37969589), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - }, - { - name: "failure - unpack error", - data: []byte{1, 2, 3, 4}, - err: errors.New("unpack error: invalid data for unpacking"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - packer := NewAbiPacker() - fl, err := packer.DecodeStreamsLookupRequest(tt.data) - assert.Equal(t, tt.expected, fl) - if tt.err != nil { - assert.Equal(t, tt.err.Error(), err.Error()) - } - }) - } -} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go index f9a3c001c66..10e77bf50ce 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go @@ -10,9 +10,13 @@ import ( "net/http" "time" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" ) const ( @@ -68,13 +72,6 @@ type MercuryData struct { State MercuryUpkeepState } -type Packer interface { - UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) - PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) - UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) - DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) -} - type MercuryConfigProvider interface { Credentials() *models.MercuryCredentials IsUpkeepAllowed(string) (interface{}, bool) @@ -113,6 +110,70 @@ func (l *StreamsLookup) IsMercuryV03() bool { return l.FeedParamKey == FeedIDs } +// IsMercuryV03UsingBlockNumber is used to distinguish the batch path. It is used for Mercury V03 only func (l *StreamsLookup) IsMercuryV03UsingBlockNumber() bool { return l.TimeParamKey == BlockNumber } + +type Packer interface { + UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) + PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) + UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) + DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) +} + +type abiPacker struct { + registryABI abi.ABI + streamsABI abi.ABI +} + +func NewAbiPacker() *abiPacker { + return &abiPacker{registryABI: core.RegistryABI, streamsABI: core.StreamsCompatibleABI} +} + +// DecodeStreamsLookupRequest decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string feedParamKey, uint256 time, byte[] extraData) +func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) { + e := p.streamsABI.Errors["StreamsLookup"] + unpack, err := e.Unpack(data) + if err != nil { + return nil, fmt.Errorf("unpack error: %w", err) + } + errorParameters := unpack.([]interface{}) + + return &StreamsLookupError{ + FeedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), + Feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), + TimeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), + Time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), + ExtraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), + }, nil +} + +func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) { + out, err := p.registryABI.Methods["checkCallback"].Outputs.UnpackValues(callbackResp) + if err != nil { + return encoding.PackUnpackDecodeFailed, false, nil, 0, nil, fmt.Errorf("%w: unpack checkUpkeep return: %s", err, hexutil.Encode(callbackResp)) + } + + upkeepNeeded := *abi.ConvertType(out[0], new(bool)).(*bool) + rawPerformData := *abi.ConvertType(out[1], new([]byte)).(*[]byte) + failureReason := *abi.ConvertType(out[2], new(uint8)).(*uint8) + gasUsed := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return encoding.NoPipelineError, upkeepNeeded, rawPerformData, failureReason, gasUsed, nil +} + +func (p *abiPacker) UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) { + out, err := p.registryABI.Methods["getUpkeepPrivilegeConfig"].Outputs.UnpackValues(resp) + if err != nil { + return nil, fmt.Errorf("%w: unpack getUpkeepPrivilegeConfig return", err) + } + + bts := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return bts, nil +} + +func (p *abiPacker) PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { + return p.registryABI.Pack("getUpkeepPrivilegeConfig", upkeepId) +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go index baa939dbecc..6095e7b9466 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go @@ -1,7 +1,17 @@ package mercury import ( + "encoding/json" + "errors" + "math/big" "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" ) func TestGenerateHMACFn(t *testing.T) { @@ -47,3 +57,186 @@ func TestGenerateHMACFn(t *testing.T) { }) } } + +func TestPacker_DecodeStreamsLookupRequest(t *testing.T) { + tests := []struct { + name string + data []byte + expected *StreamsLookupError + state uint8 + err error + }{ + { + name: "success - decode to streams lookup", + data: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000002435eb50000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + expected: &StreamsLookupError{ + FeedParamKey: "feedIdHex", + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: "blockNumber", + Time: big.NewInt(37969589), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + }, + { + name: "failure - unpack error", + data: []byte{1, 2, 3, 4}, + err: errors.New("unpack error: invalid data for unpacking"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + packer := NewAbiPacker() + fl, err := packer.DecodeStreamsLookupRequest(tt.data) + assert.Equal(t, tt.expected, fl) + if tt.err != nil { + assert.Equal(t, tt.err.Error(), err.Error()) + } + }) + } +} + +func TestPacker_UnpackGetUpkeepPrivilegeConfig(t *testing.T) { + tests := []struct { + name string + raw []byte + errored bool + }{ + { + name: "happy path", + raw: func() []byte { + b, _ := hexutil.Decode("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000177b226d657263757279456e61626c6564223a747275657d000000000000000000") + + return b + }(), + errored: false, + }, + { + name: "error empty config", + raw: func() []byte { + b, _ := hexutil.Decode("0x") + + return b + }(), + errored: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + packer := NewAbiPacker() + + b, err := packer.UnpackGetUpkeepPrivilegeConfig(test.raw) + + if !test.errored { + require.NoError(t, err, "should unpack bytes from abi encoded value") + + // the actual struct to unmarshal into is not available to this + // package so basic json encoding is the limit of the following test + var data map[string]interface{} + err = json.Unmarshal(b, &data) + + assert.NoError(t, err, "packed data should unmarshal using json encoding") + assert.Equal(t, []byte(`{"mercuryEnabled":true}`), b) + } else { + assert.NotNil(t, err, "error expected from unpack function") + } + }) + } +} + +func TestPacker_PackGetUpkeepPrivilegeConfig(t *testing.T) { + tests := []struct { + name string + upkeepId *big.Int + raw []byte + errored bool + }{ + { + name: "happy path", + upkeepId: func() *big.Int { + id, _ := new(big.Int).SetString("52236098515066839510538748191966098678939830769967377496848891145101407612976", 10) + + return id + }(), + raw: func() []byte { + b, _ := hexutil.Decode("0x19d97a94737c9583000000000000000000000001ea8ed6d0617dd5b3b87374020efaf030") + + return b + }(), + errored: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + packer := NewAbiPacker() + + b, err := packer.PackGetUpkeepPrivilegeConfig(test.upkeepId) + + if !test.errored { + require.NoError(t, err, "no error expected from packing") + + assert.Equal(t, test.raw, b, "raw bytes for output should match expected") + } else { + assert.NotNil(t, err, "error expected from packing function") + } + }) + } +} + +func TestPacker_UnpackCheckCallbackResult(t *testing.T) { + tests := []struct { + Name string + CallbackResp []byte + UpkeepNeeded bool + PerformData []byte + FailureReason uint8 + GasUsed *big.Int + ErrorString string + State uint8 + }{ + { + Name: "unpack upkeep needed", + CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: true, + PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + FailureReason: uint8(encoding.UpkeepFailureReasonNone), + GasUsed: big.NewInt(11796), + }, + { + Name: "unpack upkeep not needed", + CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: false, + PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + FailureReason: uint8(encoding.UpkeepFailureReasonUpkeepNotNeeded), + GasUsed: big.NewInt(13008), + }, + { + Name: "unpack malformed data", + CallbackResp: []byte{0, 0, 0, 23, 4, 163, 66, 91, 228, 102, 200, 84, 144, 233, 218, 44, 168, 192, 191, 253, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: false, + PerformData: nil, + ErrorString: "abi: improperly encoded boolean value: unpack checkUpkeep return: ", + State: encoding.PackUnpackDecodeFailed, + }, + } + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + packer := NewAbiPacker() + + state, needed, pd, failureReason, gasUsed, err := packer.UnpackCheckCallbackResult(test.CallbackResp) + + if test.ErrorString != "" { + assert.EqualError(t, err, test.ErrorString+hexutil.Encode(test.CallbackResp)) + } else { + assert.Nil(t, err) + } + assert.Equal(t, test.UpkeepNeeded, needed) + assert.Equal(t, test.PerformData, pd) + assert.Equal(t, test.FailureReason, failureReason) + assert.Equal(t, test.GasUsed, gasUsed) + assert.Equal(t, test.State, state) + }) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index d64a9e7ef83..e1bb69f33d8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -66,7 +66,6 @@ type UpkeepPrivilegeConfig struct { } func NewStreamsLookup( - packer mercury.Packer, mercuryConfig mercury.MercuryConfigProvider, blockSubscriber latestBlockProvider, client contextCaller, @@ -74,6 +73,8 @@ func NewStreamsLookup( lggr logger.Logger) *streams { httpClient := http.DefaultClient threadCtrl := utils.NewThreadControl() + packer := mercury.NewAbiPacker() + return &streams{ packer: packer, mercuryConfig: mercuryConfig, @@ -231,7 +232,7 @@ func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *me checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) checkResults[i].Eligible = true checkResults[i].PerformData = performData - s.lggr.Infof("at block %d upkeep %s requested time %s successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) + s.lggr.Infof("at block %d upkeep %s requested time %s CheckCallback successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) return nil } @@ -288,6 +289,7 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s var upkeepPrivilegeConfigBytes []byte upkeepPrivilegeConfigBytes, err = s.packer.UnpackGetUpkeepPrivilegeConfig(resultBytes) + if err != nil { return mercury.PackUnpackDecodeFailed, mercury.MercuryUpkeepFailureReasonNone, false, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index caa3efb974b..54124b7c1a3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -102,14 +102,12 @@ func (r *mockRegistry) CheckCallback(opts *bind.CallOpts, id *big.Int, values [] // setups up a streams object for tests. func setupStreams(t *testing.T) *streams { lggr := logger.TestLogger(t) - packer := encoding.NewAbiPacker() mercuryConfig := new(MockMercuryConfigProvider) blockSubscriber := new(MockBlockSubscriber) registry := &mockRegistry{} client := evmClientMocks.NewClient(t) streams := NewStreamsLookup( - packer, mercuryConfig, blockSubscriber, client, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go index bb05b8029fe..39862bb7539 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go @@ -94,7 +94,6 @@ func NewEvmRegistry( AllowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), } - hc := http.DefaultClient return &EvmRegistry{ @@ -115,7 +114,7 @@ func NewEvmRegistry( logEventProvider: logEventProvider, bs: blockSub, finalityDepth: finalityDepth, - streams: streams.NewStreamsLookup(packer, mercuryConfig, blockSub, client.Client(), registry, lggr), + streams: streams.NewStreamsLookup(mercuryConfig, blockSub, client.Client(), registry, lggr), } } From c274c23eabc7fade1b1a5e7649187c56f218cc77 Mon Sep 17 00:00:00 2001 From: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> Date: Wed, 13 Dec 2023 23:45:38 +0000 Subject: [PATCH 05/15] Introduce job spec flag for custom reverted pipeline (#11529) * Introduce job spec flag for custom reverted pipeline * Disable the flag for V2+ * Rename file after merge conflict --- core/services/job/job_orm_test.go | 21 +++-- core/services/job/models.go | 3 + core/services/job/orm.go | 4 +- core/services/job/validate.go | 1 + core/services/vrf/delegate.go | 3 + .../0214_add_custom_reverts_vrf.sql | 5 ++ core/testdata/testspecs/v2_specs.go | 3 + core/web/presenters/job.go | 38 ++++---- core/web/presenters/job_test.go | 87 +++++++++++++++++++ core/web/resolver/spec.go | 5 ++ core/web/resolver/spec_test.go | 3 + core/web/schema/type/spec.graphql | 1 + 12 files changed, 149 insertions(+), 25 deletions(-) create mode 100644 core/store/migrate/migrations/0214_add_custom_reverts_vrf.sql diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index c0622ba8066..768d16ddeb7 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -454,6 +454,9 @@ func TestORM_CreateJob_VRFV2(t *testing.T) { var batchFulfillmentEnabled bool require.NoError(t, db.Get(&batchFulfillmentEnabled, `SELECT batch_fulfillment_enabled FROM vrf_specs LIMIT 1`)) require.False(t, batchFulfillmentEnabled) + var customRevertsPipelineEnabled bool + require.NoError(t, db.Get(&customRevertsPipelineEnabled, `SELECT custom_reverts_pipeline_enabled FROM vrf_specs LIMIT 1`)) + require.False(t, customRevertsPipelineEnabled) var batchFulfillmentGasMultiplier float64 require.NoError(t, db.Get(&batchFulfillmentGasMultiplier, `SELECT batch_fulfillment_gas_multiplier FROM vrf_specs LIMIT 1`)) require.Equal(t, float64(1.0), batchFulfillmentGasMultiplier) @@ -514,13 +517,14 @@ func TestORM_CreateJob_VRFV2Plus(t *testing.T) { fromAddresses := []string{cltest.NewEIP55Address().String(), cltest.NewEIP55Address().String()} jb, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec( testspecs.VRFSpecParams{ - VRFVersion: vrfcommon.V2Plus, - RequestedConfsDelay: 10, - FromAddresses: fromAddresses, - ChunkSize: 25, - BackoffInitialDelay: time.Minute, - BackoffMaxDelay: time.Hour, - GasLanePrice: assets.GWei(100), + VRFVersion: vrfcommon.V2Plus, + RequestedConfsDelay: 10, + FromAddresses: fromAddresses, + ChunkSize: 25, + BackoffInitialDelay: time.Minute, + BackoffMaxDelay: time.Hour, + GasLanePrice: assets.GWei(100), + CustomRevertsPipelineEnabled: true, }). Toml()) require.NoError(t, err) @@ -534,6 +538,9 @@ func TestORM_CreateJob_VRFV2Plus(t *testing.T) { var batchFulfillmentEnabled bool require.NoError(t, db.Get(&batchFulfillmentEnabled, `SELECT batch_fulfillment_enabled FROM vrf_specs LIMIT 1`)) require.False(t, batchFulfillmentEnabled) + var customRevertsPipelineEnabled bool + require.NoError(t, db.Get(&customRevertsPipelineEnabled, `SELECT custom_reverts_pipeline_enabled FROM vrf_specs LIMIT 1`)) + require.True(t, customRevertsPipelineEnabled) var batchFulfillmentGasMultiplier float64 require.NoError(t, db.Get(&batchFulfillmentGasMultiplier, `SELECT batch_fulfillment_gas_multiplier FROM vrf_specs LIMIT 1`)) require.Equal(t, float64(1.0), batchFulfillmentGasMultiplier) diff --git a/core/services/job/models.go b/core/services/job/models.go index 3c869f76056..f60e0a12981 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -503,6 +503,9 @@ type VRFSpec struct { // for fulfilling requests. If set to true, batchCoordinatorAddress must be set in // the job spec. BatchFulfillmentEnabled bool `toml:"batchFulfillmentEnabled"` + // CustomRevertsPipelineEnabled indicates to the vrf job to run the + // custom reverted txns pipeline along with VRF listener + CustomRevertsPipelineEnabled bool `toml:"customRevertsPipelineEnabled"` // BatchFulfillmentGasMultiplier is used to determine the final gas estimate for the batch // fulfillment. BatchFulfillmentGasMultiplier tomlutils.Float64 `toml:"batchFulfillmentGasMultiplier"` diff --git a/core/services/job/orm.go b/core/services/job/orm.go index b2cf2b2af4b..6c5a879ebd0 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -326,14 +326,14 @@ func (o *orm) CreateJob(jb *Job, qopts ...pg.QOpt) error { evm_chain_id, from_addresses, poll_period, requested_confs_delay, request_timeout, chunk_size, batch_coordinator_address, batch_fulfillment_enabled, batch_fulfillment_gas_multiplier, backoff_initial_delay, backoff_max_delay, gas_lane_price, - vrf_owner_address, + vrf_owner_address, custom_reverts_pipeline_enabled, created_at, updated_at) VALUES ( :coordinator_address, :public_key, :min_incoming_confirmations, :evm_chain_id, :from_addresses, :poll_period, :requested_confs_delay, :request_timeout, :chunk_size, :batch_coordinator_address, :batch_fulfillment_enabled, :batch_fulfillment_gas_multiplier, :backoff_initial_delay, :backoff_max_delay, :gas_lane_price, - :vrf_owner_address, + :vrf_owner_address, :custom_reverts_pipeline_enabled, NOW(), NOW()) RETURNING id;` diff --git a/core/services/job/validate.go b/core/services/job/validate.go index 8f559fdb02d..b7a1dca3616 100644 --- a/core/services/job/validate.go +++ b/core/services/job/validate.go @@ -63,6 +63,7 @@ func ValidateSpec(ts string) (Type, error) { if jb.Pipeline.RequiresPreInsert() && !jb.Type.SupportsAsync() { return "", errors.Errorf("async=true tasks are not supported for %v", jb.Type) } + // spec.CustomRevertsPipelineEnabled == false, default is custom reverted txns pipeline disabled if strings.Contains(ts, "<{}>") { return "", errors.Errorf("'<{}>' syntax is not supported. Please use \"{}\" instead") diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index 03e40614a10..ba28e83bf3f 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -142,6 +142,9 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { if vrfOwner != nil { return nil, errors.New("VRF Owner is not supported for VRF V2 Plus") } + if jb.VRFSpec.CustomRevertsPipelineEnabled { + return nil, errors.New("Custom Reverted Txns Pipeline is not supported for VRF V2 Plus") + } // Get the LINKNATIVEFEED address with retries // This is needed because the RPC endpoint may be down so we need to diff --git a/core/store/migrate/migrations/0214_add_custom_reverts_vrf.sql b/core/store/migrate/migrations/0214_add_custom_reverts_vrf.sql new file mode 100644 index 00000000000..a2865fce816 --- /dev/null +++ b/core/store/migrate/migrations/0214_add_custom_reverts_vrf.sql @@ -0,0 +1,5 @@ +-- +goose Up +ALTER TABLE vrf_specs ADD COLUMN custom_reverts_pipeline_enabled boolean DEFAULT FALSE NOT NULL; + +-- +goose Down +ALTER TABLE vrf_specs DROP COLUMN custom_reverts_pipeline_enabled; diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index 00297ebdb12..e9dd25fa0f7 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -234,6 +234,7 @@ type VRFSpecParams struct { BatchCoordinatorAddress string VRFOwnerAddress string BatchFulfillmentEnabled bool + CustomRevertsPipelineEnabled bool BatchFulfillmentGasMultiplier float64 MinIncomingConfirmations int FromAddresses []string @@ -403,6 +404,7 @@ evmChainID = "%s" batchCoordinatorAddress = "%s" batchFulfillmentEnabled = %v batchFulfillmentGasMultiplier = %s +customRevertsPipelineEnabled = %v minIncomingConfirmations = %d requestedConfsDelay = %d requestTimeout = "%s" @@ -419,6 +421,7 @@ observationSource = """ toml := fmt.Sprintf(template, jobID, name, coordinatorAddress, params.EVMChainID, batchCoordinatorAddress, params.BatchFulfillmentEnabled, strconv.FormatFloat(batchFulfillmentGasMultiplier, 'f', 2, 64), + params.CustomRevertsPipelineEnabled, confirmations, params.RequestedConfsDelay, requestTimeout.String(), publicKey, chunkSize, params.BackoffInitialDelay.String(), params.BackoffMaxDelay.String(), gasLanePrice.String(), pollPeriod.String(), observationSource) diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index a2a9e70c793..e9b63c73615 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -267,6 +267,7 @@ func NewCronSpec(spec *job.CronSpec) *CronSpec { type VRFSpec struct { BatchCoordinatorAddress *ethkey.EIP55Address `json:"batchCoordinatorAddress"` BatchFulfillmentEnabled bool `json:"batchFulfillmentEnabled"` + CustomRevertsPipelineEnabled *bool `json:"customRevertsPipelineEnabled,omitempty"` BatchFulfillmentGasMultiplier float64 `json:"batchFulfillmentGasMultiplier"` CoordinatorAddress ethkey.EIP55Address `json:"coordinatorAddress"` PublicKey secp256k1.PublicKey `json:"publicKey"` @@ -281,26 +282,31 @@ type VRFSpec struct { BackoffInitialDelay models.Duration `json:"backoffInitialDelay"` BackoffMaxDelay models.Duration `json:"backoffMaxDelay"` GasLanePrice *assets.Wei `json:"gasLanePrice"` - VRFOwnerAddress *ethkey.EIP55Address `json:"vrfOwnerAddress"` + RequestedConfsDelay int64 `json:"requestedConfsDelay"` + VRFOwnerAddress *ethkey.EIP55Address `json:"vrfOwnerAddress,omitempty"` } func NewVRFSpec(spec *job.VRFSpec) *VRFSpec { return &VRFSpec{ - BatchCoordinatorAddress: spec.BatchCoordinatorAddress, - BatchFulfillmentEnabled: spec.BatchFulfillmentEnabled, - CoordinatorAddress: spec.CoordinatorAddress, - PublicKey: spec.PublicKey, - FromAddresses: spec.FromAddresses, - PollPeriod: models.MustMakeDuration(spec.PollPeriod), - MinIncomingConfirmations: spec.MinIncomingConfirmations, - CreatedAt: spec.CreatedAt, - UpdatedAt: spec.UpdatedAt, - EVMChainID: spec.EVMChainID, - ChunkSize: spec.ChunkSize, - RequestTimeout: models.MustMakeDuration(spec.RequestTimeout), - BackoffInitialDelay: models.MustMakeDuration(spec.BackoffInitialDelay), - BackoffMaxDelay: models.MustMakeDuration(spec.BackoffMaxDelay), - GasLanePrice: spec.GasLanePrice, + BatchCoordinatorAddress: spec.BatchCoordinatorAddress, + BatchFulfillmentEnabled: spec.BatchFulfillmentEnabled, + BatchFulfillmentGasMultiplier: float64(spec.BatchFulfillmentGasMultiplier), + CustomRevertsPipelineEnabled: &spec.CustomRevertsPipelineEnabled, + CoordinatorAddress: spec.CoordinatorAddress, + PublicKey: spec.PublicKey, + FromAddresses: spec.FromAddresses, + PollPeriod: models.MustMakeDuration(spec.PollPeriod), + MinIncomingConfirmations: spec.MinIncomingConfirmations, + CreatedAt: spec.CreatedAt, + UpdatedAt: spec.UpdatedAt, + EVMChainID: spec.EVMChainID, + ChunkSize: spec.ChunkSize, + RequestTimeout: models.MustMakeDuration(spec.RequestTimeout), + BackoffInitialDelay: models.MustMakeDuration(spec.BackoffInitialDelay), + BackoffMaxDelay: models.MustMakeDuration(spec.BackoffMaxDelay), + GasLanePrice: spec.GasLanePrice, + RequestedConfsDelay: spec.RequestedConfsDelay, + VRFOwnerAddress: spec.VRFOwnerAddress, } } diff --git a/core/web/presenters/job_test.go b/core/web/presenters/job_test.go index a5d6db0df18..b782452948b 100644 --- a/core/web/presenters/job_test.go +++ b/core/web/presenters/job_test.go @@ -14,11 +14,13 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/assets" + evmassets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) @@ -58,6 +60,7 @@ func TestJob(t *testing.T) { trustedBlockhashStoreBatchSize := int32(20) var specGasLimit uint32 = 1000 + vrfPubKey, _ := secp256k1.NewPublicKeyFromHex("0xede539e216e3a50e69d1c68aa9cc472085876c4002f6e1e6afee0ea63b50a78b00") testCases := []struct { name string @@ -469,6 +472,90 @@ func TestJob(t *testing.T) { } }`, }, + { + name: "vrf job spec", + job: job.Job{ + ID: 1, + Name: null.StringFrom("vrf_test"), + Type: job.VRF, + SchemaVersion: 1, + ExternalJobID: uuid.MustParse("0eec7e1d-d0d2-476c-a1a8-72dfb6633f47"), + VRFSpec: &job.VRFSpec{ + BatchCoordinatorAddress: &contractAddress, + BatchFulfillmentEnabled: true, + CustomRevertsPipelineEnabled: true, + MinIncomingConfirmations: 1, + CoordinatorAddress: contractAddress, + CreatedAt: timestamp, + UpdatedAt: timestamp, + EVMChainID: evmChainID, + FromAddresses: []ethkey.EIP55Address{fromAddress}, + PublicKey: vrfPubKey, + RequestedConfsDelay: 10, + ChunkSize: 25, + BatchFulfillmentGasMultiplier: 1, + GasLanePrice: evmassets.GWei(200), + VRFOwnerAddress: nil, + }, + PipelineSpec: &pipeline.Spec{ + ID: 1, + DotDagSource: "", + }, + }, + want: fmt.Sprintf(` + { + "data": { + "type": "jobs", + "id": "1", + "attributes": { + "name": "vrf_test", + "type": "vrf", + "schemaVersion": 1, + "maxTaskDuration": "0s", + "externalJobID": "0eec7e1d-d0d2-476c-a1a8-72dfb6633f47", + "directRequestSpec": null, + "fluxMonitorSpec": null, + "gasLimit": null, + "forwardingAllowed": false, + "cronSpec": null, + "offChainReportingOracleSpec": null, + "offChainReporting2OracleSpec": null, + "keeperSpec": null, + "vrfSpec": { + "batchCoordinatorAddress": "%s", + "batchFulfillmentEnabled": true, + "customRevertsPipelineEnabled": true, + "confirmations": 1, + "coordinatorAddress": "%s", + "createdAt": "2000-01-01T00:00:00Z", + "updatedAt": "2000-01-01T00:00:00Z", + "evmChainID": "42", + "fromAddresses": ["%s"], + "pollPeriod": "0s", + "publicKey": "%s", + "requestedConfsDelay": 10, + "requestTimeout": "0s", + "chunkSize": 25, + "batchFulfillmentGasMultiplier": 1, + "backoffInitialDelay": "0s", + "backoffMaxDelay": "0s", + "gasLanePrice": "200 gwei" + }, + "webhookSpec": null, + "blockhashStoreSpec": null, + "blockHeaderFeederSpec": null, + "bootstrapSpec": null, + "pipelineSpec": { + "id": 1, + "jobID": 0, + "dotDagSource": "" + }, + "gatewaySpec": null, + "errors": [] + } + } + }`, contractAddress, contractAddress, fromAddress, vrfPubKey.String()), + }, { name: "blockhash store spec", job: job.Job{ diff --git a/core/web/resolver/spec.go b/core/web/resolver/spec.go index 3e93ea38eab..5e8937dbb96 100644 --- a/core/web/resolver/spec.go +++ b/core/web/resolver/spec.go @@ -641,6 +641,11 @@ func (r *VRFSpecResolver) BatchFulfillmentGasMultiplier() float64 { return float64(r.spec.BatchFulfillmentGasMultiplier) } +// CustomRevertsPipelineEnabled resolves the spec's custom reverts pipeline enabled flag. +func (r *VRFSpecResolver) CustomRevertsPipelineEnabled() *bool { + return &r.spec.CustomRevertsPipelineEnabled +} + // ChunkSize resolves the spec's chunk size. func (r *VRFSpecResolver) ChunkSize() int32 { return int32(r.spec.ChunkSize) diff --git a/core/web/resolver/spec_test.go b/core/web/resolver/spec_test.go index 277aac851a6..a4fb9cdb338 100644 --- a/core/web/resolver/spec_test.go +++ b/core/web/resolver/spec_test.go @@ -581,6 +581,7 @@ func TestResolver_VRFSpec(t *testing.T) { VRFSpec: &job.VRFSpec{ BatchCoordinatorAddress: &batchCoordinatorAddress, BatchFulfillmentEnabled: true, + CustomRevertsPipelineEnabled: true, MinIncomingConfirmations: 1, CoordinatorAddress: coordinatorAddress, CreatedAt: f.Timestamp(), @@ -617,6 +618,7 @@ func TestResolver_VRFSpec(t *testing.T) { batchCoordinatorAddress batchFulfillmentEnabled batchFulfillmentGasMultiplier + customRevertsPipelineEnabled chunkSize backoffInitialDelay backoffMaxDelay @@ -644,6 +646,7 @@ func TestResolver_VRFSpec(t *testing.T) { "batchCoordinatorAddress": "0x0ad9FE7a58216242a8475ca92F222b0640E26B63", "batchFulfillmentEnabled": true, "batchFulfillmentGasMultiplier": 1, + "customRevertsPipelineEnabled": true, "chunkSize": 25, "backoffInitialDelay": "1m0s", "backoffMaxDelay": "1h0m0s", diff --git a/core/web/schema/type/spec.graphql b/core/web/schema/type/spec.graphql index 81244478637..5e24f7c3fa8 100644 --- a/core/web/schema/type/spec.graphql +++ b/core/web/schema/type/spec.graphql @@ -97,6 +97,7 @@ type VRFSpec { batchCoordinatorAddress: String batchFulfillmentEnabled: Boolean! batchFulfillmentGasMultiplier: Float! + customRevertsPipelineEnabled: Boolean chunkSize: Int! backoffInitialDelay: String! backoffMaxDelay: String! From 917b74f3a67395f4106f60f70949043f0d989bdf Mon Sep 17 00:00:00 2001 From: Anindita Ghosh <88458927+AnieeG@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:47:26 -0800 Subject: [PATCH 06/15] options to include customized pg and chainlink image (#11570) --- integration-tests/docker/test_env/cl_node.go | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index b79bb91c70e..d6ab22957a2 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/logstream" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" @@ -83,6 +84,28 @@ func WithLogStream(ls *logstream.LogStream) ClNodeOption { } } +func WithImage(image string) ClNodeOption { + return func(c *ClNode) { + c.ContainerImage = image + } +} + +func WithVersion(version string) ClNodeOption { + return func(c *ClNode) { + c.ContainerVersion = version + } +} + +func WithPgDBOptions(opts ...test_env.PostgresDbOption) ClNodeOption { + return func(c *ClNode) { + var err error + c.PostgresDb, err = test_env.NewPostgresDb(c.EnvComponent.Networks, opts...) + if err != nil { + c.t.Fatalf("failed to create postgres db: %v", err) + } + } +} + func NewClNode(networks []string, imageName, imageVersion string, nodeConfig *chainlink.Config, opts ...ClNodeOption) (*ClNode, error) { nodeDefaultCName := fmt.Sprintf("%s-%s", "cl-node", uuid.NewString()[0:8]) pgDefaultCName := fmt.Sprintf("pg-%s", nodeDefaultCName) From 7a0704f436a4be1fa0d5c607b901265056c69f2d Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 13 Dec 2023 17:45:54 -0800 Subject: [PATCH 07/15] add readme (#11535) --- core/scripts/chaincli/README.md | 91 ++++++++++++++++++++++++++ core/scripts/chaincli/handler/debug.go | 6 +- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/core/scripts/chaincli/README.md b/core/scripts/chaincli/README.md index da7aa7cc777..bcf407b684b 100644 --- a/core/scripts/chaincli/README.md +++ b/core/scripts/chaincli/README.md @@ -122,3 +122,94 @@ You can use the `grep` and `grepv` flags to filter log lines, e.g. to only show ```shell ./chaincli keeper logs --grep keepers-plugin ``` + +--- + +## ChainCLI Automation Debugging Script + +### Context + +The debugging script is a tool within ChainCLI designed to facilitate the debugging of upkeeps in Automation v21, covering both conditional and log-based scenarios. + +### Configuration + +#### Mandatory Fields + +Ensure the following fields are provided in your `.env` file: + +- `NODE_URL`: Archival node URL +- `KEEPER_REGISTRY_ADDRESS`: Address of the Keeper Registry contract. Refer to the [Supported Networks](https://docs.chain.link/chainlink-automation/overview/supported-networks#configurations) doc for addresses. + +#### Additional Fields (Streams Lookup) + +If your targeted upkeep involves streams lookup, include the following information: + +- `MERCURY_ID` +- `MERCURY_KEY` +- `MERCURY_LEGACY_URL` +- `MERCURY_URL` + +#### Tenderly Integration + +For detailed transaction simulation logs, set up Tenderly credentials. Refer to the [Tenderly Documentation](https://docs.tenderly.co/other/platform-access/how-to-generate-api-access-tokens) for creating an API key, account name, and project name. + +- `TENDERLY_KEY` +- `TENDERLY_ACCOUNT_NAME` +- `TENDERLY_PROJECT_NAME` + +### Usage + +Execute the following command based on your upkeep type: + +- For conditional upkeep: + + ```bash + go run main.go keeper debug UPKEEP_ID + ``` + +- For log trigger upkeep: + + ```bash + go run main.go keeper debug UPKEEP_ID TX_HASH LOG_INDEX + ``` + +### Checks Performed by the Debugging Script + +1. **Fetch and Sanity Check Upkeep:** + - Verify upkeep status: active, paused, or canceled + - Check upkeep balance + +2. **For Conditional Upkeep:** + - Check conditional upkeep + - Simulate `performUpkeep` + +3. **For Log Trigger Upkeep:** + - Check if the upkeep has already run for log-trigger-based upkeep + - Verify if log matches trigger configuration + - Check upkeep + - If check result indicates a streams lookup is required (TargetCheckReverted): + - Verify if the upkeep is allowed to use Mercury + - Execute Mercury request + - Execute check callback + + - Simulate `performUpkeep` + +### Examples +- Eligible and log trigger based and using mercury lookup v0.3: + + ```bash + go run main.go keeper debug 5591498142036749453487419299781783197030971023186134955311257372668222176389 0xdc6d0e547a5aa85fefa5b0f3a37e3493eafb5aeba8b5f3071ce53c9e9a539e9c 0 + ``` + +- Ineligible and conditional upkeep: + + ```bash + go run main.go keeper debug 52635131310730056105456985154251306793887717546629785340977553840883117540096 + ``` + +- Ineligible and Log does not match trigger config: + + ```bash + go run main.go keeper debug 5591498142036749453487419299781783197030971023186134955311257372668222176389 0xc0686ae85d2a7a976ef46df6c613517b9fd46f23340ac583be4e44f5c8b7a186 1 + ``` +--- \ No newline at end of file diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 3e8bd6d597e..288c7a68b80 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -111,8 +111,8 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if (upkeepInfo.Target == gethcommon.Address{}) { failCheckArgs("this upkeep does not exist on this registry", nil) } - addLink("upkeep", common.UpkeepLink(chainID, upkeepID)) - addLink("target", common.ContractExplorerLink(chainID, upkeepInfo.Target)) + addLink("upkeep link", common.UpkeepLink(chainID, upkeepID)) + addLink("upkeep contract address", common.ContractExplorerLink(chainID, upkeepInfo.Target)) if upkeepInfo.Paused { resolveIneligible("upkeep is paused") } @@ -180,7 +180,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failCheckArgs("failed to fetch tx receipt", err) } - addLink("trigger", common.ExplorerLink(chainID, txHash)) + addLink("trigger transaction", common.ExplorerLink(chainID, txHash)) blockNum = receipt.BlockNumber.Uint64() // find matching log event in tx var triggeringEvent *types.Log From f874ea608e835edd616937f852b193752cd48db8 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 14 Dec 2023 05:42:05 -0500 Subject: [PATCH 08/15] Little Docs Updates (#11569) * Little Docs Updates * Addition --- integration-tests/README.md | 6 ++++++ integration-tests/example.env | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/integration-tests/README.md b/integration-tests/README.md index c0b673fa92b..37a8fa19ed3 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -26,10 +26,16 @@ e.g. `make build_docker_image image=chainlink tag=test-tag` +You'll want to set the `CHAINLINK_IMAGE` and `CHAINLINK_VERSION` env values appropriately as well. See [example.env](./example.env) for more details. + ## Run `go test ./smoke/_test.go` +Most test files have a couple of tests, it's recommended to look into the file and focus on a specific one if possible. 90% of the time this will probably be the `Basic` test. See [ocr_test.go](./smoke/ocr_test.go) for example, which contains the `TestOCRBasic` test. + +`go test ./smoke/ocr_test.go -run TestOCRBasic` + It's generally recommended to run only one test at a time on a local machine as it needs a lot of docker containers and can peg your resources otherwise. You will see docker containers spin up on your machine for each component of the test where you can inspect logs. ## Analyze diff --git a/integration-tests/example.env b/integration-tests/example.env index 3e27ac2c7ab..bbc76bb0c09 100644 --- a/integration-tests/example.env +++ b/integration-tests/example.env @@ -30,15 +30,16 @@ export TEST_UPGRADE_VERSION="2.0.0" # Version of the Chainlink image to upgrade ########## Network Settings ########## +# If running on a live network, you must set the following values # Select a pre-defined network(s) export SELECTED_NETWORKS="SIMULATED" - # General private values that will be retrieved when running on non-simulated networks export EVM_URLS="wss://evm.url,wss://other.url" # Comma-sparated list of websocket URLs to use when running on live networks export EVM_HTTP_URLS="https://evm.url,https://other.url" # Comma-sparated list of HTTP URLs to use when running on live networks export EVM_KEYS="private,funding,keys" # Comma-separated list of private keys to use when running on live networks # Specific private values retrieved when running on specified chains +# Will override the general values above if the SELECTED_NETWORKS contains the network name # Goerli export GOERLI_URLS="wss://goerli.io/ws" export GOERLI_HTTP_URLS="http://goerli.io/ws" From 71e1a796ee3450cd1a0cb20fff8e5f1d46690972 Mon Sep 17 00:00:00 2001 From: Lei Date: Thu, 14 Dec 2023 03:12:22 -0800 Subject: [PATCH 09/15] add unit test to cover 2 check results (#11490) --- .../v21/mercury/streams/streams_test.go | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index 54124b7c1a3..40f32751161 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -521,6 +521,69 @@ func TestStreams_StreamsLookup(t *testing.T) { }, }, }, + { + name: "two CheckResults: Mercury success E2E and No Mercury because of insufficient balance", + input: []ocr2keepers.CheckResult{ + { + PerformData: []byte{}, + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: 26046145, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonInsufficientBalance), + }, + { + PerformData: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: blockNum, + }, + IneligibilityReason: uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted), + }, + }, + blobs: map[string]string{ + "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000": "0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa3", + "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000": "0x0006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d", + }, + cachedAdminCfg: false, + extraData: hexutil.MustDecode("0x0000000000000000000000000000000000000064"), + callbackNeeded: true, + checkCallbackResp: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063a400000000000000000000000000000000000000000000000000000000000006e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + values: [][]byte{hexutil.MustDecode("0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa3"), hexutil.MustDecode("0x0006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d")}, + expectedResults: []ocr2keepers.CheckResult{ + { + Eligible: false, + PerformData: []byte{}, + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: 26046145, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonInsufficientBalance), + }, + { + Eligible: true, + PerformData: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: blockNum, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonNone), + }, + }, + hasPermission: true, + v3: false, + registry: &mockRegistry{ + GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return []byte(`{"mercuryEnabled":true}`), nil + }, + CheckCallbackFn: func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return iregistry21.CheckCallback{ + UpkeepNeeded: true, + PerformData: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + }, nil + }, + }, + }, { name: "success - happy path no cache - v0.3", input: []ocr2keepers.CheckResult{ From e427abbf4baf54e39281ee83495352c2de0ff02d Mon Sep 17 00:00:00 2001 From: frank zhu Date: Thu, 14 Dec 2023 06:41:44 -0800 Subject: [PATCH 10/15] add CI test for core/scripts (#11466) * add CI test for core/scripts * refactor * refactor golangci-lint build binary step * add ubig pkg * refactor based on suggestions --- .github/actions/golangci-lint/action.yml | 7 +--- .github/workflows/ci-chaincli.yml | 22 ------------ .github/workflows/ci-scripts.yml | 46 ++++++++++++++++++++++++ core/scripts/functions/src/fetching.go | 1 + 4 files changed, 48 insertions(+), 28 deletions(-) delete mode 100644 .github/workflows/ci-chaincli.yml create mode 100644 .github/workflows/ci-scripts.yml diff --git a/.github/actions/golangci-lint/action.yml b/.github/actions/golangci-lint/action.yml index 055960ff282..0047c6a54bd 100644 --- a/.github/actions/golangci-lint/action.yml +++ b/.github/actions/golangci-lint/action.yml @@ -42,14 +42,9 @@ runs: shell: bash run: mkdir -p core/web/assets && touch core/web/assets/index.html - name: Build binary - if: ${{ inputs.go-directory == '.' }} - shell: bash - run: go build ./... - - name: Build binary - if: ${{ inputs.go-directory != '.' }} working-directory: ${{ inputs.go-directory }} shell: bash - run: go build + run: go build ./... - name: golangci-lint uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 with: diff --git a/.github/workflows/ci-chaincli.yml b/.github/workflows/ci-chaincli.yml deleted file mode 100644 index 8a9ab03d766..00000000000 --- a/.github/workflows/ci-chaincli.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: chaincli CI - -on: - push: - pull_request: - -jobs: - golangci: - if: ${{ github.event_name == 'pull_request' || github.event_name == 'schedule' }} - name: chaincli-lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - name: Golang Lint - uses: ./.github/actions/golangci-lint - with: - name: chaincli-lint - go-directory: core/scripts/chaincli - go-version-file: core/scripts/go.mod - go-module-file: core/scripts/go.sum - gc-basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} - gc-host: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/ci-scripts.yml b/.github/workflows/ci-scripts.yml new file mode 100644 index 00000000000..e83c22520cd --- /dev/null +++ b/.github/workflows/ci-scripts.yml @@ -0,0 +1,46 @@ +name: CI Scripts + +on: + push: + pull_request: + +jobs: + golangci: + if: ${{ github.event_name == 'pull_request' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Golang Lint + uses: ./.github/actions/golangci-lint + with: + name: scripts-lint + go-directory: core/scripts + go-version-file: core/scripts/go.mod + go-module-file: core/scripts/go.sum + gc-basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + gc-host: ${{ secrets.GRAFANA_CLOUD_HOST }} + + test: + if: ${{ github.event_name == 'pull_request' }} + name: scripts-test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Setup Go + uses: ./.github/actions/setup-go + with: + go-version-file: core/scripts/go.mod + go-module-file: core/scripts/go.sum + - name: Run Tests + shell: bash + working-directory: core/scripts + run: go test ./... + - name: Collect Metrics + if: always() + id: collect-gha-metrics + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 + with: + basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} + this-job-name: scripts-test + continue-on-error: true diff --git a/core/scripts/functions/src/fetching.go b/core/scripts/functions/src/fetching.go index 9be624a40b6..cbe491fff5e 100644 --- a/core/scripts/functions/src/fetching.go +++ b/core/scripts/functions/src/fetching.go @@ -11,6 +11,7 @@ import ( "github.com/urfave/cli" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" + ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) From b2e163b4e5079e204489e29cd8587878233ab656 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 14 Dec 2023 18:14:31 +0100 Subject: [PATCH 11/15] bump npm packages (#11572) --- contracts/package.json | 26 +-- contracts/pnpm-lock.yaml | 404 +++++++++++++++++++-------------------- 2 files changed, 215 insertions(+), 215 deletions(-) diff --git a/contracts/package.json b/contracts/package.json index abbf722140c..4dabbdb0509 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -43,49 +43,49 @@ "@nomiclabs/hardhat-etherscan": "^3.1.7", "@nomiclabs/hardhat-waffle": "2.0.6", "@openzeppelin/hardhat-upgrades": "1.28.0", - "@scroll-tech/contracts": "0.1.0", "@openzeppelin/test-helpers": "^0.5.16", "@typechain/ethers-v5": "^7.2.0", "@typechain/hardhat": "^7.0.0", "@types/cbor": "5.0.1", - "@types/chai": "^4.3.10", + "@types/chai": "^4.3.11", "@types/debug": "^4.1.12", "@types/deep-equal-in-any-order": "^1.0.3", - "@types/mocha": "^10.0.4", - "@types/node": "^16.18.61", - "@typescript-eslint/eslint-plugin": "^6.11.0", - "@typescript-eslint/parser": "^6.11.0", + "@types/mocha": "^10.0.6", + "@types/node": "^16.18.68", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", "abi-to-sol": "^0.6.6", "cbor": "^5.2.0", "chai": "^4.3.10", "debug": "^4.3.4", - "eslint": "^8.53.0", - "eslint-config-prettier": "^9.0.0", + "eslint": "^8.55.0", + "eslint-config-prettier": "^9.1.0", "deep-equal-in-any-order": "^2.0.6", "eslint-plugin-prettier": "^5.0.1", "ethereum-waffle": "^3.4.4", "ethers": "~5.7.2", - "hardhat": "~2.19.1", + "hardhat": "~2.19.2", "hardhat-abi-exporter": "^2.10.1", "hardhat-contract-sizer": "^2.10.0", "hardhat-gas-reporter": "^1.0.9", "hardhat-ignore-warnings": "^0.2.6", "istanbul": "^0.4.5", "moment": "^2.29.4", - "prettier": "^3.1.0", + "prettier": "^3.1.1", "prettier-plugin-solidity": "1.2.0", "rlp": "^2.2.7", "solhint": "^4.0.0", "solhint-plugin-chainlink-solidity": "git+https://github.com/smartcontractkit/chainlink-solhint-rules.git#v1.2.0", "solhint-plugin-prettier": "^0.1.0", "solidity-coverage": "^0.8.5", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "tslib": "^2.6.2", "typechain": "^8.2.1", - "typescript": "^5.2.2" + "typescript": "^5.3.3" }, "dependencies": { - "@eth-optimism/contracts": "0.5.37", + "@eth-optimism/contracts": "0.6.0", + "@scroll-tech/contracts": "0.1.0", "@openzeppelin/contracts": "4.9.3", "@openzeppelin/contracts-upgradeable": "4.9.3" } diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index dffcb0a7c7c..1b18835dc2e 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -9,14 +9,17 @@ overrides: dependencies: '@eth-optimism/contracts': - specifier: 0.5.37 - version: 0.5.37(ethers@5.7.2) + specifier: 0.6.0 + version: 0.6.0(ethers@5.7.2) '@openzeppelin/contracts': specifier: 4.9.3 version: 4.9.3 '@openzeppelin/contracts-upgradeable': specifier: 4.9.3 version: 4.9.3 + '@scroll-tech/contracts': + specifier: 0.1.0 + version: 0.1.0 devDependencies: '@ethereum-waffle/mock-contract': @@ -39,37 +42,34 @@ devDependencies: version: 5.7.0 '@nomicfoundation/hardhat-network-helpers': specifier: ^1.0.9 - version: 1.0.9(hardhat@2.19.1) + version: 1.0.9(hardhat@2.19.2) '@nomiclabs/hardhat-ethers': specifier: ^2.2.3 - version: 2.2.3(ethers@5.7.2)(hardhat@2.19.1) + version: 2.2.3(ethers@5.7.2)(hardhat@2.19.2) '@nomiclabs/hardhat-etherscan': specifier: ^3.1.7 - version: 3.1.7(hardhat@2.19.1) + version: 3.1.7(hardhat@2.19.2) '@nomiclabs/hardhat-waffle': specifier: 2.0.6 - version: 2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.8)(ethereum-waffle@3.4.4)(ethers@5.7.2)(hardhat@2.19.1) + version: 2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.8)(ethereum-waffle@3.4.4)(ethers@5.7.2)(hardhat@2.19.2) '@openzeppelin/hardhat-upgrades': specifier: 1.28.0 - version: 1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.1) + version: 1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.2) '@openzeppelin/test-helpers': specifier: ^0.5.16 version: 0.5.16(bn.js@4.12.0) - '@scroll-tech/contracts': - specifier: 0.1.0 - version: 0.1.0 '@typechain/ethers-v5': specifier: ^7.2.0 - version: 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.2.2) + version: 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.3.3) '@typechain/hardhat': specifier: ^7.0.0 - version: 7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@7.2.0)(ethers@5.7.2)(hardhat@2.19.1)(typechain@8.3.2) + version: 7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@7.2.0)(ethers@5.7.2)(hardhat@2.19.2)(typechain@8.3.2) '@types/cbor': specifier: 5.0.1 version: 5.0.1 '@types/chai': - specifier: ^4.3.10 - version: 4.3.10 + specifier: ^4.3.11 + version: 4.3.11 '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -77,17 +77,17 @@ devDependencies: specifier: ^1.0.3 version: 1.0.3 '@types/mocha': - specifier: ^10.0.4 - version: 10.0.4 + specifier: ^10.0.6 + version: 10.0.6 '@types/node': - specifier: ^16.18.61 - version: 16.18.61 + specifier: ^16.18.68 + version: 16.18.68 '@typescript-eslint/eslint-plugin': - specifier: ^6.11.0 - version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2) + specifier: ^6.14.0 + version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: ^6.11.0 - version: 6.11.0(eslint@8.53.0)(typescript@5.2.2) + specifier: ^6.14.0 + version: 6.14.0(eslint@8.55.0)(typescript@5.3.3) abi-to-sol: specifier: ^0.6.6 version: 0.6.6 @@ -104,32 +104,32 @@ devDependencies: specifier: ^2.0.6 version: 2.0.6 eslint: - specifier: ^8.53.0 - version: 8.53.0 + specifier: ^8.55.0 + version: 8.55.0 eslint-config-prettier: - specifier: ^9.0.0 - version: 9.0.0(eslint@8.53.0) + specifier: ^9.1.0 + version: 9.1.0(eslint@8.55.0) eslint-plugin-prettier: specifier: ^5.0.1 - version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.1.0) + version: 5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.1) ethereum-waffle: specifier: ^3.4.4 - version: 3.4.4(typescript@5.2.2) + version: 3.4.4(typescript@5.3.3) ethers: specifier: ~5.7.2 version: 5.7.2 hardhat: - specifier: ~2.19.1 - version: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + specifier: ~2.19.2 + version: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) hardhat-abi-exporter: specifier: ^2.10.1 - version: 2.10.1(hardhat@2.19.1) + version: 2.10.1(hardhat@2.19.2) hardhat-contract-sizer: specifier: ^2.10.0 - version: 2.10.0(hardhat@2.19.1) + version: 2.10.0(hardhat@2.19.2) hardhat-gas-reporter: specifier: ^1.0.9 - version: 1.0.9(debug@4.3.4)(hardhat@2.19.1) + version: 1.0.9(debug@4.3.4)(hardhat@2.19.2) hardhat-ignore-warnings: specifier: ^0.2.6 version: 0.2.9 @@ -140,11 +140,11 @@ devDependencies: specifier: ^2.29.4 version: 2.29.4 prettier: - specifier: ^3.1.0 - version: 3.1.0 + specifier: ^3.1.1 + version: 3.1.1 prettier-plugin-solidity: specifier: 1.2.0 - version: 1.2.0(prettier@3.1.0) + version: 1.2.0(prettier@3.1.1) rlp: specifier: ^2.2.7 version: 2.2.7 @@ -156,22 +156,22 @@ devDependencies: version: github.com/smartcontractkit/chainlink-solhint-rules/cfc50b32f95b730304a50deb2e27e88d87115874 solhint-plugin-prettier: specifier: ^0.1.0 - version: 0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.0) + version: 0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.1) solidity-coverage: specifier: ^0.8.5 - version: 0.8.5(hardhat@2.19.1) + version: 0.8.5(hardhat@2.19.2) ts-node: - specifier: ^10.9.1 - version: 10.9.1(@types/node@16.18.61)(typescript@5.2.2) + specifier: ^10.9.2 + version: 10.9.2(@types/node@16.18.68)(typescript@5.3.3) tslib: specifier: ^2.6.2 version: 2.6.2 typechain: specifier: ^8.2.1 - version: 8.3.2(typescript@5.2.2) + version: 8.3.2(typescript@5.3.3) typescript: - specifier: ^5.2.2 - version: 5.2.2 + specifier: ^5.3.3 + version: 5.3.3 packages: @@ -327,13 +327,13 @@ packages: deprecated: Please use @ensdomains/ens-contracts dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.55.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.53.0 + eslint: 8.55.0 eslint-visitor-keys: 3.4.3 dev: true @@ -342,8 +342,8 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/eslintrc@2.1.3: - resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} + /@eslint/eslintrc@2.1.4: + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 @@ -359,17 +359,17 @@ packages: - supports-color dev: true - /@eslint/js@8.53.0: - resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} + /@eslint/js@8.55.0: + resolution: {integrity: sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@eth-optimism/contracts@0.5.37(ethers@5.7.2): - resolution: {integrity: sha512-HbNUUDIM1dUAM0hWPfGp3l9/Zte40zi8QhVbUSIwdYRA7jG7cZgbteqavrjW8wwFqxkWX9IrtA0KAR7pNlSAIQ==} + /@eth-optimism/contracts@0.6.0(ethers@5.7.2): + resolution: {integrity: sha512-vQ04wfG9kMf1Fwy3FEMqH2QZbgS0gldKhcBeBUPfO8zu68L61VI97UDXmsMQXzTsEAxK8HnokW3/gosl4/NW3w==} peerDependencies: ethers: ^5 dependencies: - '@eth-optimism/core-utils': 0.10.1 + '@eth-optimism/core-utils': 0.12.0 '@ethersproject/abstract-provider': 5.7.0 '@ethersproject/abstract-signer': 5.7.0 ethers: 5.7.2 @@ -378,8 +378,8 @@ packages: - utf-8-validate dev: false - /@eth-optimism/core-utils@0.10.1: - resolution: {integrity: sha512-IJvG5UtYvyz6An9QdohlCLoeB3NBFxx2lRJKlPzvYYlfugUNNCHsajRIWIwJTcPRRma0WPd46JUsKACLJDdNrA==} + /@eth-optimism/core-utils@0.12.0: + resolution: {integrity: sha512-qW+7LZYCz7i8dRa7SRlUKIo1VBU8lvN0HeXCxJR+z+xtMzMQpPds20XJNCMclszxYQHkXY00fOT6GvFw9ZL6nw==} dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/abstract-provider': 5.7.0 @@ -415,7 +415,7 @@ packages: - utf-8-validate dev: true - /@ethereum-waffle/compiler@3.4.4(typescript@5.2.2): + /@ethereum-waffle/compiler@3.4.4(typescript@5.3.3): resolution: {integrity: sha512-RUK3axJ8IkD5xpWjWoJgyHclOeEzDLQFga6gKpeGxiS/zBu+HB0W2FvsrrLalTFIaPw/CGYACRBSIxqiCqwqTQ==} engines: {node: '>=10.0'} dependencies: @@ -429,7 +429,7 @@ packages: node-fetch: 2.6.7 solc: 0.6.12 ts-generator: 0.1.1 - typechain: 3.0.0(typescript@5.2.2) + typechain: 3.0.0(typescript@5.3.3) transitivePeerDependencies: - bufferutil - encoding @@ -1040,13 +1040,13 @@ packages: - utf-8-validate dev: true - /@nomicfoundation/hardhat-network-helpers@1.0.9(hardhat@2.19.1): + /@nomicfoundation/hardhat-network-helpers@1.0.9(hardhat@2.19.2): resolution: {integrity: sha512-OXWCv0cHpwLUO2u7bFxBna6dQtCC2Gg/aN/KtJLO7gmuuA28vgmVKYFRCDUqrbjujzgfwQ2aKyZ9Y3vSmDqS7Q==} peerDependencies: hardhat: ^2.9.5 dependencies: ethereumjs-util: 7.1.5 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) dev: true /@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.0: @@ -1155,17 +1155,17 @@ packages: '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.0 dev: true - /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.19.1): + /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.19.2): resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==} peerDependencies: ethers: ^5.0.0 hardhat: ^2.0.0 dependencies: ethers: 5.7.2 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) dev: true - /@nomiclabs/hardhat-etherscan@3.1.7(hardhat@2.19.1): + /@nomiclabs/hardhat-etherscan@3.1.7(hardhat@2.19.2): resolution: {integrity: sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==} peerDependencies: hardhat: ^2.0.4 @@ -1176,7 +1176,7 @@ packages: chalk: 2.4.2 debug: 4.3.4(supports-color@8.1.1) fs-extra: 7.0.1 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) lodash: 4.17.21 semver: 6.3.0 table: 6.8.1 @@ -1185,7 +1185,7 @@ packages: - supports-color dev: true - /@nomiclabs/hardhat-waffle@2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.8)(ethereum-waffle@3.4.4)(ethers@5.7.2)(hardhat@2.19.1): + /@nomiclabs/hardhat-waffle@2.0.6(@nomiclabs/hardhat-ethers@2.2.3)(@types/sinon-chai@3.2.8)(ethereum-waffle@3.4.4)(ethers@5.7.2)(hardhat@2.19.2): resolution: {integrity: sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg==} peerDependencies: '@nomiclabs/hardhat-ethers': ^2.0.0 @@ -1194,11 +1194,11 @@ packages: ethers: ^5.0.0 hardhat: ^2.0.0 dependencies: - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.1) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.2) '@types/sinon-chai': 3.2.8 - ethereum-waffle: 3.4.4(typescript@5.2.2) + ethereum-waffle: 3.4.4(typescript@5.3.3) ethers: 5.7.2 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) dev: true /@openzeppelin/contract-loader@0.6.3: @@ -1229,7 +1229,7 @@ packages: - encoding dev: true - /@openzeppelin/hardhat-upgrades@1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.1): + /@openzeppelin/hardhat-upgrades@1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.2): resolution: {integrity: sha512-7sb/Jf+X+uIufOBnmHR0FJVWuxEs2lpxjJnLNN6eCJCP8nD0v+Ot5lTOW2Qb/GFnh+fLvJtEkhkowz4ZQ57+zQ==} hasBin: true peerDependencies: @@ -1242,15 +1242,15 @@ packages: '@nomiclabs/harhdat-etherscan': optional: true dependencies: - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.1) - '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.19.1) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.2) + '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.19.2) '@openzeppelin/defender-base-client': 1.49.0(debug@4.3.4) '@openzeppelin/platform-deploy-client': 0.8.0(debug@4.3.4) '@openzeppelin/upgrades-core': 1.30.1 chalk: 4.1.2 debug: 4.3.4(supports-color@8.1.1) ethers: 5.7.2 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) proper-lockfile: 4.1.2 transitivePeerDependencies: - encoding @@ -1341,12 +1341,12 @@ packages: config-chain: 1.1.13 dev: true - /@prettier/sync@0.3.0(prettier@3.1.0): + /@prettier/sync@0.3.0(prettier@3.1.1): resolution: {integrity: sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw==} peerDependencies: prettier: ^3.0.0 dependencies: - prettier: 3.1.0 + prettier: 3.1.1 dev: true /@resolver-engine/core@0.3.3: @@ -1392,7 +1392,7 @@ packages: /@scroll-tech/contracts@0.1.0: resolution: {integrity: sha512-aBbDOc3WB/WveZdpJYcrfvMYMz7ZTEiW8M9XMJLba8p9FAR5KGYB/cV+8+EUsq3MKt7C1BfR+WnXoTVdvwIY6w==} - dev: true + dev: false /@scure/base@1.1.1: resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} @@ -1667,10 +1667,10 @@ packages: typechain: ^3.0.0 dependencies: ethers: 5.7.2 - typechain: 3.0.0(typescript@5.2.2) + typechain: 3.0.0(typescript@5.3.3) dev: true - /@typechain/ethers-v5@7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.2.2): + /@typechain/ethers-v5@7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.3.3): resolution: {integrity: sha512-jfcmlTvaaJjng63QsT49MT6R1HFhtO/TBMWbyzPFSzMmVIqb2tL6prnKBs4ZJrSvmgIXWy+ttSjpaxCTq8D/Tw==} peerDependencies: '@ethersproject/abi': ^5.0.0 @@ -1685,12 +1685,12 @@ packages: '@ethersproject/providers': 5.7.2 ethers: 5.7.2 lodash: 4.17.21 - ts-essentials: 7.0.3(typescript@5.2.2) - typechain: 8.3.2(typescript@5.2.2) - typescript: 5.2.2 + ts-essentials: 7.0.3(typescript@5.3.3) + typechain: 8.3.2(typescript@5.3.3) + typescript: 5.3.3 dev: true - /@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@7.2.0)(ethers@5.7.2)(hardhat@2.19.1)(typechain@8.3.2): + /@typechain/hardhat@7.0.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@7.2.0)(ethers@5.7.2)(hardhat@2.19.2)(typechain@8.3.2): resolution: {integrity: sha512-XB79i5ewg9Met7gMVGfgVkmypicbnI25T5clJBEooMoW2161p4zvKFpoS2O+lBppQyMrPIZkdvl2M3LMDayVcA==} peerDependencies: '@ethersproject/abi': ^5.4.7 @@ -1702,23 +1702,23 @@ packages: dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/providers': 5.7.2 - '@typechain/ethers-v5': 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.2.2) + '@typechain/ethers-v5': 7.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.3.2)(typescript@5.3.3) ethers: 5.7.2 fs-extra: 9.1.0 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) - typechain: 8.3.2(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) + typechain: 8.3.2(typescript@5.3.3) dev: true /@types/bn.js@4.11.6: resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/bn.js@5.1.1: resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/cacheable-request@6.0.2: @@ -1726,24 +1726,24 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 16.18.61 + '@types/node': 16.18.68 '@types/responselike': 1.0.0 dev: true /@types/cbor@5.0.1: resolution: {integrity: sha512-zVqJy2KzusZPLOgyGJDnOIbu3DxIGGqxYbEwtEEe4Z+la8jwIhOyb+GMrlHafs5tvKruwf8f8qOYP6zTvse/pw==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true - /@types/chai@4.3.10: - resolution: {integrity: sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==} + /@types/chai@4.3.11: + resolution: {integrity: sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==} dev: true /@types/concat-stream@1.6.1: resolution: {integrity: sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/debug@4.1.12: @@ -1763,7 +1763,7 @@ packages: /@types/form-data@0.0.33: resolution: {integrity: sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/glob@7.1.1: @@ -1771,7 +1771,7 @@ packages: dependencies: '@types/events': 3.0.0 '@types/minimatch': 3.0.3 - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/http-cache-semantics@4.0.1: @@ -1785,7 +1785,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/lru-cache@5.1.1: @@ -1799,11 +1799,11 @@ packages: /@types/mkdirp@0.5.2: resolution: {integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true - /@types/mocha@10.0.4: - resolution: {integrity: sha512-xKU7bUjiFTIttpWaIZ9qvgg+22O1nmbA+HRxdlR+u6TWsGfmFdXrheJoK4fFxrHNVIOBDvDNKZG+LYBpMHpX3w==} + /@types/mocha@10.0.6: + resolution: {integrity: sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==} dev: true /@types/ms@0.7.31: @@ -1813,7 +1813,7 @@ packages: /@types/node-fetch@2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 form-data: 3.0.1 dev: true @@ -1825,8 +1825,8 @@ packages: resolution: {integrity: sha512-7xHmXm/QJ7cbK2laF+YYD7gb5MggHIIQwqyjin3bpEGiSuvScMQ5JZZXPvRipi1MwckTQbJZROMns/JxdnIL1Q==} dev: true - /@types/node@16.18.61: - resolution: {integrity: sha512-k0N7BqGhJoJzdh6MuQg1V1ragJiXTh8VUBAZTWjJ9cUq23SG0F0xavOwZbhiP4J3y20xd6jxKx+xNUhkMAi76Q==} + /@types/node@16.18.68: + resolution: {integrity: sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==} dev: true /@types/node@8.10.66: @@ -1836,7 +1836,7 @@ packages: /@types/pbkdf2@3.1.0: resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/prettier@2.7.1: @@ -1850,26 +1850,26 @@ packages: /@types/readable-stream@2.3.15: resolution: {integrity: sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 safe-buffer: 5.1.2 dev: true /@types/resolve@0.0.8: resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/secp256k1@4.0.3: resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} dependencies: - '@types/node': 16.18.61 + '@types/node': 16.18.68 dev: true /@types/semver@7.5.0: @@ -1879,7 +1879,7 @@ packages: /@types/sinon-chai@3.2.8: resolution: {integrity: sha512-d4ImIQbT/rKMG8+AXpmcan5T2/PNeSjrYhvkwet6z0p8kzYtfgA32xzOBlbU0yqJfq+/0Ml805iFoODO0LP5/g==} dependencies: - '@types/chai': 4.3.10 + '@types/chai': 4.3.11 '@types/sinon': 10.0.13 dev: true @@ -1893,8 +1893,8 @@ packages: resolution: {integrity: sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==} dev: true - /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==} + /@typescript-eslint/eslint-plugin@6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-1ZJBykBCXaSHG94vMMKmiHoL0MhNHKSVlcHVYZNw+BKxufhqQVTOawNpwwI1P5nIFZ/4jLVop0mcY6mJJDFNaw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -1905,25 +1905,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.8.0 - '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/parser': 6.14.0(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.14.0 + '@typescript-eslint/type-utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.14.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 + eslint: 8.55.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) - typescript: 5.2.2 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==} + /@typescript-eslint/parser@6.14.0(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-QjToC14CKacd4Pa7JK4GeB/vHmWFJckec49FR4hmIRf97+KXole0T97xxu9IFiPxVQ1DBWrQ5wreLwAGwWAVQA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1932,27 +1932,27 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/scope-manager': 6.14.0 + '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.14.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 - typescript: 5.2.2 + eslint: 8.55.0 + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@6.11.0: - resolution: {integrity: sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==} + /@typescript-eslint/scope-manager@6.14.0: + resolution: {integrity: sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/visitor-keys': 6.14.0 dev: true - /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==} + /@typescript-eslint/type-utils@6.14.0(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-x6OC9Q7HfYKqjnuNu5a7kffIYs3No30isapRBJl1iCHLitD8O0lFbRcVGiOcuyN837fqXzPZ1NS10maQzZMKqw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1961,23 +1961,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) - '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2) + '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.53.0 - ts-api-utils: 1.0.3(typescript@5.2.2) - typescript: 5.2.2 + eslint: 8.55.0 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.11.0: - resolution: {integrity: sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==} + /@typescript-eslint/types@6.14.0: + resolution: {integrity: sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.11.0(typescript@5.2.2): - resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==} + /@typescript-eslint/typescript-estree@6.14.0(typescript@5.3.3): + resolution: {integrity: sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1985,42 +1985,42 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/visitor-keys': 6.11.0 + '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/visitor-keys': 6.14.0 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - ts-api-utils: 1.0.3(typescript@5.2.2) - typescript: 5.2.2 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.2.2): - resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==} + /@typescript-eslint/utils@6.14.0(eslint@8.55.0)(typescript@5.3.3): + resolution: {integrity: sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 6.11.0 - '@typescript-eslint/types': 6.11.0 - '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2) - eslint: 8.53.0 + '@typescript-eslint/scope-manager': 6.14.0 + '@typescript-eslint/types': 6.14.0 + '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3) + eslint: 8.55.0 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@6.11.0: - resolution: {integrity: sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==} + /@typescript-eslint/visitor-keys@6.14.0: + resolution: {integrity: sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.11.0 + '@typescript-eslint/types': 6.14.0 eslint-visitor-keys: 3.4.3 dev: true @@ -4714,16 +4714,16 @@ packages: source-map: 0.2.0 dev: true - /eslint-config-prettier@9.0.0(eslint@8.53.0): - resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + /eslint-config-prettier@9.1.0(eslint@8.55.0): + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.53.0 + eslint: 8.55.0 dev: true - /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.1.0): + /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.1.0)(eslint@8.55.0)(prettier@3.1.1): resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -4737,9 +4737,9 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.53.0 - eslint-config-prettier: 9.0.0(eslint@8.53.0) - prettier: 3.1.0 + eslint: 8.55.0 + eslint-config-prettier: 9.1.0(eslint@8.55.0) + prettier: 3.1.1 prettier-linter-helpers: 1.0.0 synckit: 0.8.5 dev: true @@ -4757,15 +4757,15 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.53.0: - resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} + /eslint@8.55.0: + resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0) '@eslint-community/regexpp': 4.8.0 - '@eslint/eslintrc': 2.1.3 - '@eslint/js': 8.53.0 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.55.0 '@humanwhocodes/config-array': 0.11.13 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -5060,13 +5060,13 @@ packages: '@scure/bip39': 1.1.0 dev: true - /ethereum-waffle@3.4.4(typescript@5.2.2): + /ethereum-waffle@3.4.4(typescript@5.3.3): resolution: {integrity: sha512-PA9+jCjw4WC3Oc5ocSMBj5sXvueWQeAbvCA+hUlb6oFgwwKyq5ka3bWQ7QZcjzIX+TdFkxP4IbFmoY2D8Dkj9Q==} engines: {node: '>=10.0'} hasBin: true dependencies: '@ethereum-waffle/chai': 3.4.4 - '@ethereum-waffle/compiler': 3.4.4(typescript@5.2.2) + '@ethereum-waffle/compiler': 3.4.4(typescript@5.3.3) '@ethereum-waffle/mock-contract': 3.4.4 '@ethereum-waffle/provider': 3.4.4 ethers: 5.7.2 @@ -6218,7 +6218,7 @@ packages: har-schema: 2.0.0 dev: true - /hardhat-abi-exporter@2.10.1(hardhat@2.19.1): + /hardhat-abi-exporter@2.10.1(hardhat@2.19.2): resolution: {integrity: sha512-X8GRxUTtebMAd2k4fcPyVnCdPa6dYK4lBsrwzKP5yiSq4i+WadWPIumaLfce53TUf/o2TnLpLOduyO1ylE2NHQ==} engines: {node: '>=14.14.0'} peerDependencies: @@ -6226,28 +6226,28 @@ packages: dependencies: '@ethersproject/abi': 5.7.0 delete-empty: 3.0.0 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) dev: true - /hardhat-contract-sizer@2.10.0(hardhat@2.19.1): + /hardhat-contract-sizer@2.10.0(hardhat@2.19.2): resolution: {integrity: sha512-QiinUgBD5MqJZJh1hl1jc9dNnpJg7eE/w4/4GEnrcmZJJTDbVFNe3+/3Ep24XqISSkYxRz36czcPHKHd/a0dwA==} peerDependencies: hardhat: ^2.0.0 dependencies: chalk: 4.1.2 cli-table3: 0.6.3 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) strip-ansi: 6.0.1 dev: true - /hardhat-gas-reporter@1.0.9(debug@4.3.4)(hardhat@2.19.1): + /hardhat-gas-reporter@1.0.9(debug@4.3.4)(hardhat@2.19.2): resolution: {integrity: sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==} peerDependencies: hardhat: ^2.0.2 dependencies: array-uniq: 1.0.3 eth-gas-reporter: 0.2.27(debug@4.3.4) - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) sha1: 1.1.1 transitivePeerDependencies: - '@codechecks/client' @@ -6264,8 +6264,8 @@ packages: solidity-comments: 0.0.2 dev: true - /hardhat@2.19.1(ts-node@10.9.1)(typescript@5.2.2): - resolution: {integrity: sha512-bsWa63g1GB78ZyMN08WLhFElLPA+J+pShuKD1BFO2+88g3l+BL3R07vj9deIi9dMbssxgE714Gof1dBEDGqnCw==} + /hardhat@2.19.2(ts-node@10.9.2)(typescript@5.3.3): + resolution: {integrity: sha512-CRU3+0Cc8Qh9UpxKd8cLADDPes7ZDtKj4dTK+ERtLBomEzhRPLWklJn4VKOwjre9/k8GNd/e9DYxpfuzcxbXPQ==} hasBin: true peerDependencies: ts-node: '*' @@ -6320,9 +6320,9 @@ packages: solc: 0.7.3(debug@4.3.4) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 - ts-node: 10.9.1(@types/node@16.18.61)(typescript@5.2.2) + ts-node: 10.9.2(@types/node@16.18.68)(typescript@5.3.3) tsort: 0.0.1 - typescript: 5.2.2 + typescript: 5.3.3 undici: 5.19.1 uuid: 8.3.2 ws: 7.5.9 @@ -8909,14 +8909,14 @@ packages: dev: true optional: true - /prettier-plugin-solidity@1.2.0(prettier@3.1.0): + /prettier-plugin-solidity@1.2.0(prettier@3.1.1): resolution: {integrity: sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA==} engines: {node: '>=16'} peerDependencies: prettier: '>=2.3.0' dependencies: '@solidity-parser/parser': 0.16.2 - prettier: 3.1.0 + prettier: 3.1.1 semver: 7.5.4 solidity-comments-extractor: 0.0.7 dev: true @@ -8927,8 +8927,8 @@ packages: hasBin: true dev: true - /prettier@3.1.0: - resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==} + /prettier@3.1.1: + resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} engines: {node: '>=14'} hasBin: true dev: true @@ -9960,16 +9960,16 @@ packages: - debug dev: true - /solhint-plugin-prettier@0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.0): + /solhint-plugin-prettier@0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.1): resolution: {integrity: sha512-SDOTSM6tZxZ6hamrzl3GUgzF77FM6jZplgL2plFBclj/OjKP8Z3eIPojKU73gRr0MvOS8ACZILn8a5g0VTz/Gw==} peerDependencies: prettier: ^3.0.0 prettier-plugin-solidity: ^1.0.0 dependencies: - '@prettier/sync': 0.3.0(prettier@3.1.0) - prettier: 3.1.0 + '@prettier/sync': 0.3.0(prettier@3.1.1) + prettier: 3.1.1 prettier-linter-helpers: 1.0.0 - prettier-plugin-solidity: 1.2.0(prettier@3.1.0) + prettier-plugin-solidity: 1.2.0(prettier@3.1.1) dev: true /solhint@4.0.0: @@ -10114,7 +10114,7 @@ packages: solidity-comments-win32-x64-msvc: 0.0.2 dev: true - /solidity-coverage@0.8.5(hardhat@2.19.1): + /solidity-coverage@0.8.5(hardhat@2.19.2): resolution: {integrity: sha512-6C6N6OV2O8FQA0FWA95FdzVH+L16HU94iFgg5wAFZ29UpLFkgNI/DRR2HotG1bC0F4gAc/OMs2BJI44Q/DYlKQ==} hasBin: true peerDependencies: @@ -10130,7 +10130,7 @@ packages: ghost-testrpc: 0.0.2 global-modules: 2.0.0 globby: 10.0.2 - hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.2(ts-node@10.9.2)(typescript@5.3.3) jsonschema: 1.4.0 lodash: 4.17.21 mocha: 10.2.0 @@ -10719,13 +10719,13 @@ packages: engines: {node: '>=0.10.0'} dev: true - /ts-api-utils@1.0.3(typescript@5.2.2): + /ts-api-utils@1.0.3(typescript@5.3.3): resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} engines: {node: '>=16.13.0'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.2.2 + typescript: 5.3.3 dev: true /ts-command-line-args@2.5.1: @@ -10742,20 +10742,20 @@ packages: resolution: {integrity: sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ==} dev: true - /ts-essentials@6.0.7(typescript@5.2.2): + /ts-essentials@6.0.7(typescript@5.3.3): resolution: {integrity: sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 5.2.2 + typescript: 5.3.3 dev: true - /ts-essentials@7.0.3(typescript@5.2.2): + /ts-essentials@7.0.3(typescript@5.3.3): resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 5.2.2 + typescript: 5.3.3 dev: true /ts-generator@0.1.1: @@ -10773,8 +10773,8 @@ packages: ts-essentials: 1.0.4 dev: true - /ts-node@10.9.1(@types/node@16.18.61)(typescript@5.2.2): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + /ts-node@10.9.2(@types/node@16.18.68)(typescript@5.3.3): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: '@swc/core': '>=1.2.50' @@ -10792,14 +10792,14 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 16.18.61 + '@types/node': 16.18.68 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.2.2 + typescript: 5.3.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -10884,7 +10884,7 @@ packages: resolution: {integrity: sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==} dev: true - /typechain@3.0.0(typescript@5.2.2): + /typechain@3.0.0(typescript@5.3.3): resolution: {integrity: sha512-ft4KVmiN3zH4JUFu2WJBrwfHeDf772Tt2d8bssDTo/YcckKW2D+OwFrHXRC6hJvO3mHjFQTihoMV6fJOi0Hngg==} hasBin: true dependencies: @@ -10893,14 +10893,14 @@ packages: fs-extra: 7.0.1 js-sha3: 0.8.0 lodash: 4.17.21 - ts-essentials: 6.0.7(typescript@5.2.2) + ts-essentials: 6.0.7(typescript@5.3.3) ts-generator: 0.1.1 transitivePeerDependencies: - supports-color - typescript dev: true - /typechain@8.3.2(typescript@5.2.2): + /typechain@8.3.2(typescript@5.3.3): resolution: {integrity: sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==} hasBin: true peerDependencies: @@ -10915,8 +10915,8 @@ packages: mkdirp: 1.0.4 prettier: 2.8.8 ts-command-line-args: 2.5.1 - ts-essentials: 7.0.3(typescript@5.2.2) - typescript: 5.2.2 + ts-essentials: 7.0.3(typescript@5.3.3) + typescript: 5.3.3 transitivePeerDependencies: - supports-color dev: true @@ -10969,8 +10969,8 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true - /typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} engines: {node: '>=14.17'} hasBin: true dev: true From 7dd42eb8a8326c6c440e4346fe24c1aebc1cf076 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 14 Dec 2023 13:49:14 -0500 Subject: [PATCH 12/15] Fix Makefile Oversight (#11568) * Adds MAKE Command for Building Plugin Image * Makefile Oversight --- integration-tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/Makefile b/integration-tests/Makefile index 86a266a996a..5e34b059b68 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -196,7 +196,7 @@ build_docker_image: # tag: the tag for the chainlink image being built, example: tag=latest # example usage: make build_docker_image image=chainlink tag=latest .PHONY: build_plugin_docker_image -build_docker_image: +build_plugin_docker_image: docker build -f ../plugins/chainlink.Dockerfile --build-arg COMMIT_SHA=$(git rev-parse HEAD) --build-arg CHAINLINK_USER=chainlink -t localhost:5000/chainlink:develop ../ # image: the name for the chainlink image being built, example: image=chainlink From 3c299706fdfd76703853cab27db97b567a8a06cd Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Thu, 14 Dec 2023 16:14:00 -0500 Subject: [PATCH 13/15] Extract relay from evm (#11537) * Extract relay from evm * Replace type with string * Update helpers_test.go * ci --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- core/chains/evm/config/toml/config.go | 17 ++++++++--------- core/chains/evm/log/helpers_test.go | 9 +++++++-- core/chains/evm/types/types.go | 5 ++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 16ad74dbca2..ff6a9872840 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -21,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/store/models" configutils "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) @@ -107,7 +106,7 @@ func (cs EVMConfigs) totalChains() int { } return total } -func (cs EVMConfigs) Chains(ids ...relay.ChainID) (r []commontypes.ChainStatus, total int, err error) { +func (cs EVMConfigs) Chains(ids ...string) (r []commontypes.ChainStatus, total int, err error) { total = cs.totalChains() for _, ch := range cs { if ch == nil { @@ -154,7 +153,7 @@ func (cs EVMConfigs) NodeStatus(name string) (commontypes.NodeStatus, error) { for i := range cs { for _, n := range cs[i].Nodes { if n.Name != nil && *n.Name == name { - return nodeStatus(n, relay.ChainID(cs[i].ChainID.String())) + return nodeStatus(n, cs[i].ChainID.String()) } } } @@ -179,7 +178,7 @@ func legacyNode(n *Node, chainID *big.Big) (v2 types.Node) { return } -func nodeStatus(n *Node, chainID relay.ChainID) (commontypes.NodeStatus, error) { +func nodeStatus(n *Node, chainID string) (commontypes.NodeStatus, error) { var s commontypes.NodeStatus s.ChainID = chainID s.Name = *n.Name @@ -191,7 +190,7 @@ func nodeStatus(n *Node, chainID relay.ChainID) (commontypes.NodeStatus, error) return s, nil } -func (cs EVMConfigs) nodes(id relay.ChainID) (ns EVMNodes) { +func (cs EVMConfigs) nodes(id string) (ns EVMNodes) { for _, c := range cs { if c.ChainID.String() == id { return c.Nodes @@ -200,7 +199,7 @@ func (cs EVMConfigs) nodes(id relay.ChainID) (ns EVMNodes) { return nil } -func (cs EVMConfigs) Nodes(chainID relay.ChainID) (ns []types.Node, err error) { +func (cs EVMConfigs) Nodes(chainID string) (ns []types.Node, err error) { evmID, err := ChainIDInt64(chainID) if err != nil { return nil, fmt.Errorf("invalid evm chain id %q : %w", chainID, err) @@ -220,14 +219,14 @@ func (cs EVMConfigs) Nodes(chainID relay.ChainID) (ns []types.Node, err error) { return } -func (cs EVMConfigs) NodeStatuses(chainIDs ...relay.ChainID) (ns []commontypes.NodeStatus, err error) { +func (cs EVMConfigs) NodeStatuses(chainIDs ...string) (ns []commontypes.NodeStatus, err error) { if len(chainIDs) == 0 { for i := range cs { for _, n := range cs[i].Nodes { if n == nil { continue } - n2, err := nodeStatus(n, relay.ChainID(cs[i].ChainID.String())) + n2, err := nodeStatus(n, cs[i].ChainID.String()) if err != nil { return nil, err } @@ -816,6 +815,6 @@ func (n *Node) SetFrom(f *Node) { } } -func ChainIDInt64(cid relay.ChainID) (int64, error) { +func ChainIDInt64(cid string) (int64, error) { return strconv.ParseInt(cid, 10, 64) } diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index cea2e361184..de8ff024b84 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -30,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" @@ -42,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) type broadcasterHelper struct { @@ -105,7 +105,12 @@ func newBroadcasterHelperWithEthClient(t *testing.T, ethClient evmclient.Client, LogBroadcaster: &log.NullBroadcaster{}, MailMon: mailMon, }) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(cc) + + m := make(map[string]legacyevm.Chain) + for _, r := range cc.Slice() { + m[r.Chain().ID().String()] = r.Chain() + } + legacyChains := legacyevm.NewLegacyChains(m, cc.AppConfig().EVMConfigs()) pipelineHelper := cltest.NewJobPipelineV2(t, config.WebServer(), config.JobPipeline(), config.Database(), legacyChains, db, kst, nil, nil) return &broadcasterHelper{ diff --git a/core/chains/evm/types/types.go b/core/chains/evm/types/types.go index aa43806da1f..ec1134de2b4 100644 --- a/core/chains/evm/types/types.go +++ b/core/chains/evm/types/types.go @@ -14,14 +14,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" ubig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils/big" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/utils" ) type Configs interface { - Chains(ids ...relay.ChainID) ([]types.ChainStatus, int, error) + Chains(ids ...string) ([]types.ChainStatus, int, error) Node(name string) (Node, error) - Nodes(chainID relay.ChainID) (nodes []Node, err error) + Nodes(chainID string) (nodes []Node, err error) NodeStatus(name string) (types.NodeStatus, error) } From 4b0608a94c5b15f0168e4b87817564d16637e27d Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 14 Dec 2023 16:32:50 -0500 Subject: [PATCH 14/15] Lower All Runner Sizes (#11539) * Lower all runner sizes * Remove skips * Make Most of it Free * Return Solana and builds back to normal * Reset others --- .github/workflows/integration-tests.yml | 18 ++++++++--------- .../smoke/automation_test.go_test_list.json | 20 +++++++++---------- .../smoke/keeper_test.go_test_list.json | 20 +++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 6ffd1a14ff2..0c754807f31 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -207,8 +207,8 @@ jobs: id: build-test-matrix-list run: | cd ./integration-tests - MATRIX_JSON_AUTOMATION=$(./scripts/buildTestMatrixList.sh ./smoke/automation_test.go automation ubuntu20.04-8cores-32GB 1) - MATRIX_JSON_KEEPER=$(./scripts/buildTestMatrixList.sh ./smoke/keeper_test.go keeper ubuntu20.04-8cores-32GB 1) + MATRIX_JSON_AUTOMATION=$(./scripts/buildTestMatrixList.sh ./smoke/automation_test.go automation ubuntu-latest 1) + MATRIX_JSON_KEEPER=$(./scripts/buildTestMatrixList.sh ./smoke/keeper_test.go keeper ubuntu-latest 1) COMBINED_ARRAY=$(jq -c -n "$MATRIX_JSON_AUTOMATION + $MATRIX_JSON_KEEPER") # if we running a PR against the develop branch we should only run the automation tests unless we are in the merge group event @@ -324,13 +324,13 @@ jobs: pyroscope_env: "" - name: ocr nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest run: -run TestOCRJobReplacement file: ocr pyroscope_env: ci-smoke-ocr-evm-simulated - name: ocr-geth nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest run: -run TestOCRBasic file: ocr client: geth @@ -403,25 +403,25 @@ jobs: pyroscope_env: "" - name: vrf nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-vrf-evm-simulated - name: vrfv2 nodes: 1 run: -run TestVRFv2MultipleSendingKeys file: vrfv2 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-vrf2-evm-simulated - name: vrfv2plus nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-vrf2plus-evm-simulated - name: forwarder_ocr nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-forwarder-ocr-evm-simulated - name: forwarders_ocr2 nodes: 1 - os: ubuntu20.04-8cores-32GB + os: ubuntu-latest pyroscope_env: ci-smoke-forwarder-ocr-evm-simulated runs-on: ${{ matrix.product.os }} name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} diff --git a/integration-tests/smoke/automation_test.go_test_list.json b/integration-tests/smoke/automation_test.go_test_list.json index ad6de592ede..da214a0d282 100644 --- a/integration-tests/smoke/automation_test.go_test_list.json +++ b/integration-tests/smoke/automation_test.go_test_list.json @@ -2,7 +2,7 @@ "tests": [ { "name": "TestAutomationBasic", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 6 }, { @@ -10,43 +10,43 @@ }, { "name": "TestAutomationAddFunds", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationPauseUnPause", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationRegisterUpkeep", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationPauseRegistry", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationKeeperNodesDown", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationPerformSimulation", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestAutomationCheckPerformGasLimit", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestUpdateCheckData", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 2 } ] -} +} \ No newline at end of file diff --git a/integration-tests/smoke/keeper_test.go_test_list.json b/integration-tests/smoke/keeper_test.go_test_list.json index b874bc65ee0..b2f4aa00659 100644 --- a/integration-tests/smoke/keeper_test.go_test_list.json +++ b/integration-tests/smoke/keeper_test.go_test_list.json @@ -2,42 +2,42 @@ "tests": [ { "name": "TestKeeperBasicSmoke", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperBlockCountPerTurn", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperSimulation", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { "name": "TestKeeperCheckPerformGasLimit", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperRegisterUpkeep", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperAddFunds", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperRemove", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 3 }, { "name": "TestKeeperPauseRegistry", - "label": "ubuntu20.04-16cores-64GB", + "label": "ubuntu-latest", "nodes": 2 }, { @@ -45,7 +45,7 @@ }, { "name": "TestKeeperNodeDown", - "label": "ubuntu20.04-32cores-128GB", + "label": "ubuntu-latest", "nodes": 3 }, { @@ -58,4 +58,4 @@ "name": "TestKeeperJobReplacement" } ] -} +} \ No newline at end of file From 7773d031434095f21c81eb790efbb873ba95b4f7 Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:18:31 -0600 Subject: [PATCH 15/15] Remove direct references to TXM DB from external component tests (#11538) * Removed direct references to TXM DB from external component tests * Fixed linting --- core/chains/evm/txmgr/evm_tx_store.go | 65 ++++++++++++++++++ core/chains/evm/txmgr/evm_tx_store_test.go | 13 +++- core/cmd/evm_transaction_commands_test.go | 68 +++++++++++-------- core/cmd/shell_local_test.go | 2 +- core/internal/cltest/cltest.go | 10 ++- core/internal/testutils/testutils.go | 9 --- core/services/keeper/upkeep_executer_test.go | 7 +- .../promreporter/prom_reporter_test.go | 3 +- core/services/vrf/delegate_test.go | 10 +-- core/web/eth_keys_controller_test.go | 17 +++-- core/web/evm_transfer_controller_test.go | 21 ++++-- 11 files changed, 162 insertions(+), 63 deletions(-) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index f9014163675..9b46ab7a80c 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -71,6 +71,11 @@ type TestEvmTxStore interface { InsertTxAttempt(attempt *TxAttempt) error LoadTxesAttempts(etxs []*Tx, qopts ...pg.QOpt) error GetFatalTransactions(ctx context.Context) (txes []*Tx, err error) + GetAllTxes(ctx context.Context) (txes []*Tx, err error) + GetAllTxAttempts(ctx context.Context) (attempts []TxAttempt, err error) + CountTxesByStateAndSubject(ctx context.Context, state txmgrtypes.TxState, subject uuid.UUID) (count int, err error) + FindTxesByFromAddressAndState(ctx context.Context, fromAddress common.Address, state string) (txes []*Tx, err error) + UpdateTxAttemptBroadcastBeforeBlockNum(ctx context.Context, id int64, blockNum uint) error } type evmTxStore struct { @@ -2011,6 +2016,66 @@ func (o *evmTxStore) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Co return txes, pkgerrors.Wrap(err, "FindTxesWithAttemptsAndReceiptsByIdsAndState failed") } +// For testing only, get all txes in the DB +func (o *evmTxStore) GetAllTxes(ctx context.Context) (txes []*Tx, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + var dbEtxs []DbEthTx + sql := "SELECT * FROM evm.txes" + err = qq.Select(&dbEtxs, sql) + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + return txes, err +} + +// For testing only, get all tx attempts in the DB +func (o *evmTxStore) GetAllTxAttempts(ctx context.Context) (attempts []TxAttempt, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + var dbAttempts []DbEthTxAttempt + sql := "SELECT * FROM evm.tx_attempts" + err = qq.Select(&dbAttempts, sql) + attempts = dbEthTxAttemptsToEthTxAttempts(dbAttempts) + return attempts, err +} + +func (o *evmTxStore) CountTxesByStateAndSubject(ctx context.Context, state txmgrtypes.TxState, subject uuid.UUID) (count int, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + sql := "SELECT COUNT(*) FROM evm.txes WHERE state = $1 AND subject = $2" + err = qq.Get(&count, sql, state, subject) + return count, err +} + +func (o *evmTxStore) FindTxesByFromAddressAndState(ctx context.Context, fromAddress common.Address, state string) (txes []*Tx, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + sql := "SELECT * FROM evm.txes WHERE from_address = $1 AND state = $2" + var dbEtxs []DbEthTx + err = qq.Select(&dbEtxs, sql, fromAddress, state) + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + return txes, err +} + +func (o *evmTxStore) UpdateTxAttemptBroadcastBeforeBlockNum(ctx context.Context, id int64, blockNum uint) error { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + sql := "UPDATE evm.tx_attempts SET broadcast_before_block_num = $1 WHERE eth_tx_id = $2" + _, err := qq.Exec(sql, blockNum, id) + return err +} + // Returns a context that contains the values of the provided context, // and which is canceled when either the provided contextg or TxStore parent context is canceled. func (o *evmTxStore) mergeContexts(ctx context.Context) (context.Context, context.CancelFunc) { diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index 0b4287b6f6c..6c19026c1da 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -1811,7 +1811,7 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := newTestChainScopedConfig(t) - txStore := newTxStore(t, db, cfg.Database()) + txStore := txmgr.NewTxStore(db, logger.Test(t), cfg.Database()) ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() evmtest.NewEthClientMockWithDefaultChain(t) _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) @@ -1822,7 +1822,7 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { for i := 0; i < 5; i++ { mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, txRequestWithStrategy(strategy1)) } - testutils.AssertCountPerSubject(t, db, int64(5), subject1) + AssertCountPerSubject(t, txStore, int64(5), subject1) }) t.Run("prunes if queue has exceeded capacity", func(t *testing.T) { @@ -1831,6 +1831,13 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { for i := 0; i < 5; i++ { mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, txRequestWithStrategy(strategy2)) } - testutils.AssertCountPerSubject(t, db, int64(3), subject2) + AssertCountPerSubject(t, txStore, int64(3), subject2) }) } + +func AssertCountPerSubject(t *testing.T, txStore txmgr.TestEvmTxStore, expected int64, subject uuid.UUID) { + t.Helper() + count, err := txStore.CountTxesByStateAndSubject(testutils.Context(t), "unstarted", subject) + require.NoError(t, err) + require.Equal(t, int(expected), count) +} diff --git a/core/cmd/evm_transaction_commands_test.go b/core/cmd/evm_transaction_commands_test.go index 6c079a12495..5c80a7d74a0 100644 --- a/core/cmd/evm_transaction_commands_test.go +++ b/core/cmd/evm_transaction_commands_test.go @@ -19,6 +19,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -156,7 +158,8 @@ func TestShell_SendEther_From_Txm(t *testing.T) { ) client, r := app.NewShellAndRenderer() db := app.GetSqlxDB() - + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) set := flag.NewFlagSet("sendether", 0) flagSetApplyFromAction(client.SendEther, set, "") @@ -170,21 +173,26 @@ func TestShell_SendEther_From_Txm(t *testing.T) { assert.NoError(t, client.SendEther(c)) - dbEvmTx := txmgr.DbEthTx{} - require.NoError(t, db.Get(&dbEvmTx, `SELECT * FROM evm.txes`)) - require.Equal(t, "100.500000000000000000", dbEvmTx.Value.String()) - require.Equal(t, fromAddress, dbEvmTx.FromAddress) - require.Equal(t, to, dbEvmTx.ToAddress.String()) + evmTxes, err := txStore.GetAllTxes(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, evmTxes, 1) + evmTx := evmTxes[0] + value := assets.Eth(evmTx.Value) + require.Equal(t, "100.500000000000000000", value.String()) + require.Equal(t, fromAddress, evmTx.FromAddress) + require.Equal(t, to, evmTx.ToAddress.String()) output := *r.Renders[0].(*cmd.EthTxPresenter) - assert.Equal(t, &dbEvmTx.FromAddress, output.From) - assert.Equal(t, &dbEvmTx.ToAddress, output.To) - assert.Equal(t, dbEvmTx.Value.String(), output.Value) - assert.Equal(t, fmt.Sprintf("%d", *dbEvmTx.Nonce), output.Nonce) - - var dbEvmTxAttempt txmgr.DbEthTxAttempt - require.NoError(t, db.Get(&dbEvmTxAttempt, `SELECT * FROM evm.tx_attempts`)) - assert.Equal(t, dbEvmTxAttempt.Hash, output.Hash) + assert.Equal(t, &evmTx.FromAddress, output.From) + assert.Equal(t, &evmTx.ToAddress, output.To) + assert.Equal(t, value.String(), output.Value) + assert.Equal(t, fmt.Sprintf("%d", *evmTx.Sequence), output.Nonce) + + attempts, err := txStore.GetAllTxAttempts(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, attempts, 1) + assert.Equal(t, attempts[0].Hash, output.Hash) + } func TestShell_SendEther_From_Txm_WEI(t *testing.T) { @@ -216,6 +224,8 @@ func TestShell_SendEther_From_Txm_WEI(t *testing.T) { ) client, r := app.NewShellAndRenderer() db := app.GetSqlxDB() + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) set := flag.NewFlagSet("sendether", 0) flagSetApplyFromAction(client.SendEther, set, "") @@ -236,19 +246,23 @@ func TestShell_SendEther_From_Txm_WEI(t *testing.T) { assert.NoError(t, client.SendEther(c)) - dbEvmTx := txmgr.DbEthTx{} - require.NoError(t, db.Get(&dbEvmTx, `SELECT * FROM evm.txes`)) - require.Equal(t, "1.000000000000000000", dbEvmTx.Value.String()) - require.Equal(t, fromAddress, dbEvmTx.FromAddress) - require.Equal(t, to, dbEvmTx.ToAddress.String()) + evmTxes, err := txStore.GetAllTxes(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, evmTxes, 1) + evmTx := evmTxes[0] + value := assets.Eth(evmTx.Value) + require.Equal(t, "1.000000000000000000", value.String()) + require.Equal(t, fromAddress, evmTx.FromAddress) + require.Equal(t, to, evmTx.ToAddress.String()) output := *r.Renders[0].(*cmd.EthTxPresenter) - assert.Equal(t, &dbEvmTx.FromAddress, output.From) - assert.Equal(t, &dbEvmTx.ToAddress, output.To) - assert.Equal(t, dbEvmTx.Value.String(), output.Value) - assert.Equal(t, fmt.Sprintf("%d", *dbEvmTx.Nonce), output.Nonce) - - var dbEvmTxAttempt txmgr.DbEthTxAttempt - require.NoError(t, db.Get(&dbEvmTxAttempt, `SELECT * FROM evm.tx_attempts`)) - assert.Equal(t, dbEvmTxAttempt.Hash, output.Hash) + assert.Equal(t, &evmTx.FromAddress, output.From) + assert.Equal(t, &evmTx.ToAddress, output.To) + assert.Equal(t, value.String(), output.Value) + assert.Equal(t, fmt.Sprintf("%d", *evmTx.Sequence), output.Nonce) + + attempts, err := txStore.GetAllTxAttempts(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, attempts, 1) + assert.Equal(t, attempts[0].Hash, output.Hash) } diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 56da90d811c..400a8e3e75a 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -420,7 +420,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { assert.NoError(t, c.RebroadcastTransactions(ctx)) - cltest.AssertEthTxAttemptCountStays(t, app.GetSqlxDB(), 1) + cltest.AssertEthTxAttemptCountStays(t, txStore, 1) }) } } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index c5e1fc5e4d0..8ebf4b84b46 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -974,16 +974,14 @@ func AssertPipelineRunsStays(t testing.TB, pipelineSpecID int32, db *sqlx.DB, wa } // AssertEthTxAttemptCountStays asserts that the number of tx attempts remains at the provided value -func AssertEthTxAttemptCountStays(t testing.TB, db *sqlx.DB, want int) []int64 { +func AssertEthTxAttemptCountStays(t testing.TB, txStore txmgr.TestEvmTxStore, want int) []int64 { g := gomega.NewWithT(t) var txaIds []int64 - var err error - g.Consistently(func() []int64 { - txaIds = make([]int64, 0) - err = db.Select(&txaIds, `SELECT ID FROM evm.tx_attempts ORDER BY id ASC`) + g.Consistently(func() []txmgr.TxAttempt { + attempts, err := txStore.GetAllTxAttempts(testutils.Context(t)) assert.NoError(t, err) - return txaIds + return attempts }, AssertNoActionTimeout, DBPollingInterval).Should(gomega.HaveLen(want)) return txaIds } diff --git a/core/internal/testutils/testutils.go b/core/internal/testutils/testutils.go index 6ffd873d092..81c09b1c362 100644 --- a/core/internal/testutils/testutils.go +++ b/core/internal/testutils/testutils.go @@ -425,15 +425,6 @@ func AssertCount(t *testing.T, db *sqlx.DB, tableName string, expected int64) { require.Equal(t, expected, count) } -func AssertCountPerSubject(t *testing.T, db *sqlx.DB, expected int64, subject uuid.UUID) { - t.Helper() - var count int64 - err := db.Get(&count, `SELECT COUNT(*) FROM evm.txes - WHERE state = 'unstarted' AND subject = $1;`, subject) - require.NoError(t, err) - require.Equal(t, expected, count) -} - func NewTestFlagSet() *flag.FlagSet { return flag.NewFlagSet("test", flag.PanicOnError) } diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 61ccca956f4..42e79425cff 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -335,7 +335,12 @@ func Test_UpkeepExecuter_PerformsUpkeep_Error(t *testing.T) { executer.OnNewLongestChain(testutils.Context(t), &head) g.Eventually(wasCalled.Load).Should(gomega.Equal(true)) - cltest.AssertCountStays(t, db, "evm.txes", 0) + + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + txes, err := txStore.GetAllTxes(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, txes, 0) } func ptr[T any](t T) *T { return &t } diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index 7b9930e4daa..60d6d9388fa 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -26,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/promreporter" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func newHead() evmtypes.Head { @@ -113,7 +112,7 @@ func Test_PromReporter_OnNewLongestChain(t *testing.T) { etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress) cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress) cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress) - require.NoError(t, utils.JustError(db.Exec(`UPDATE evm.tx_attempts SET broadcast_before_block_num = 7 WHERE eth_tx_id = $1`, etx.ID))) + require.NoError(t, txStore.UpdateTxAttemptBroadcastBeforeBlockNum(testutils.Context(t), etx.ID, 7)) head := newHead() reporter.OnNewLongestChain(testutils.Context(t), &head) diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 663080c86a0..1d9f97d136d 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -404,11 +404,13 @@ func TestDelegate_InvalidLog(t *testing.T) { } } - // Ensure we have NOT queued up an eth transaction - var ethTxes []txmgr.DbEthTx - err = vuni.prm.GetQ().Select(ðTxes, `SELECT * FROM evm.txes;`) + db := pgtest.NewSqlxDB(t) + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + + txes, err := txStore.GetAllTxes(testutils.Context(t)) require.NoError(t, err) - require.Len(t, ethTxes, 0) + require.Len(t, txes, 0) } func TestFulfilledCheck(t *testing.T) { diff --git a/core/web/eth_keys_controller_test.go b/core/web/eth_keys_controller_test.go index d0ad27262f2..739af5820c9 100644 --- a/core/web/eth_keys_controller_test.go +++ b/core/web/eth_keys_controller_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" webpresenters "github.com/smartcontractkit/chainlink/v2/core/web/presenters" @@ -411,10 +412,12 @@ func TestETHKeysController_ChainSuccess_ResetWithAbandon(t *testing.T) { }) assert.NoError(t, err) - var count int - err = app.GetSqlxDB().Get(&count, `SELECT count(*) FROM evm.txes WHERE from_address = $1 AND state = 'fatal_error'`, addr) + db := app.GetSqlxDB() + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg.Database()) + + txes, err := txStore.FindTxesByFromAddressAndState(testutils.Context(t), addr, "fatal_error") require.NoError(t, err) - assert.Equal(t, 0, count) + require.Len(t, txes, 0) client := app.NewHTTPClient(nil) chainURL := url.URL{Path: "/v2/keys/evm/chain"} @@ -437,10 +440,12 @@ func TestETHKeysController_ChainSuccess_ResetWithAbandon(t *testing.T) { assert.Equal(t, cltest.FixtureChainID.String(), updatedKey.EVMChainID.String()) assert.Equal(t, false, updatedKey.Disabled) - var s string - err = app.GetSqlxDB().Get(&s, `SELECT error FROM evm.txes WHERE from_address = $1 AND state = 'fatal_error'`, addr) + txes, err = txStore.FindTxesByFromAddressAndState(testutils.Context(t), addr, "fatal_error") require.NoError(t, err) - assert.Equal(t, "abandoned", s) + require.Len(t, txes, 1) + + tx := txes[0] + assert.Equal(t, "abandoned", tx.Error.String) } func TestETHKeysController_ChainFailure_InvalidAbandon(t *testing.T) { diff --git a/core/web/evm_transfer_controller_test.go b/core/web/evm_transfer_controller_test.go index dd083a8cd63..cff996dd21c 100644 --- a/core/web/evm_transfer_controller_test.go +++ b/core/web/evm_transfer_controller_test.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -18,6 +20,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/web" @@ -68,7 +72,7 @@ func TestTransfersController_CreateSuccess_From(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Len(t, errors.Errors, 0) - cltest.AssertCount(t, app.GetSqlxDB(), "evm.txes", 1) + validateTxCount(t, app.GetSqlxDB(), 1) } func TestTransfersController_CreateSuccess_From_WEI(t *testing.T) { @@ -109,7 +113,7 @@ func TestTransfersController_CreateSuccess_From_WEI(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Len(t, errors.Errors, 0) - cltest.AssertCount(t, app.GetSqlxDB(), "evm.txes", 1) + validateTxCount(t, app.GetSqlxDB(), 1) } func TestTransfersController_CreateSuccess_From_BalanceMonitorDisabled(t *testing.T) { @@ -155,7 +159,7 @@ func TestTransfersController_CreateSuccess_From_BalanceMonitorDisabled(t *testin assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Len(t, errors.Errors, 0) - cltest.AssertCount(t, app.GetSqlxDB(), "evm.txes", 1) + validateTxCount(t, app.GetSqlxDB(), 1) } func TestTransfersController_TransferZeroAddressError(t *testing.T) { @@ -323,7 +327,7 @@ func TestTransfersController_CreateSuccess_eip1559(t *testing.T) { err = web.ParseJSONAPIResponse(cltest.ParseResponseBody(t, resp), &resource) assert.NoError(t, err) - cltest.AssertCount(t, app.GetSqlxDB(), "evm.txes", 1) + validateTxCount(t, app.GetSqlxDB(), 1) // check returned data assert.NotEmpty(t, resource.Hash) @@ -393,3 +397,12 @@ func TestTransfersController_FindTxAttempt(t *testing.T) { assert.ErrorContains(t, err, "context canceled") }) } + +func validateTxCount(t *testing.T, db *sqlx.DB, count int) { + cfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + + txes, err := txStore.GetAllTxes(testutils.Context(t)) + require.NoError(t, err) + require.Len(t, txes, count) +}